I have an (angular 5 based) a-frame app that has two scenes. I want to keep track of application state such as config parms using the aframe-state-component component. I want to access the cumulative state across both scenes. However, empirically it seems that every time I switch to the other scene all the variables in the state are reset to their initial state.
Also, the fact that you can access the state variables using a statement like:
AFRAME.scenes[0].systems.state.state.score
suggests to me that the state is tied to one scene only (scenes[0] being equal to the current scene).
At first I thought it was an angular issue as the supposed singleton service where I initialized state was being initialized on every scene transfer. But I discovered that linking using the angular router:
this.router.navigate([(evt.target as any).previousSibling.getAttribute('link').href, {}])
Instead of the default:
window.location = this.data.href
Fixed that problem. So it now appears to be an issue with A-frame.
Is the notion that this component can only be used intra-scene and not inter-scene a correct assumption?
Note: I'm also experimenting with angular-redux/store and it seems to have no problem retaining state across scenes, but I'd rather use aframe-state-component as it seems simpler.
You can store in localStorage and restore on load. Something like:
window.addEventListener('beforeunload', () => {
localStorage.setItem('state', JSON.parse(state));
});
initialState: localStorage.getItem('state')
? JSON.parse(localStorage.getItem('state'))
: {
// Initial state...
}
EDIT: Realized you meant for in-app scene changes? You can do something similar. When a scene is unloaded, store it somewhere, then state component checks if it exists?
The state component doesn't seem to store the states, so It would work if you had two scenes on one page:
<a-scene></a-scene>
<a-scene visible="false"></a-scene>
and switch them by setting the visible attribute. I also shows that AFRAME.scenes refer to scenes on the page, since you can log AFRAME.scenes[0] and AFRAME.scenes[1] in the above "example" (or in this fiddle).
Related
Hello this is my first question. I am trying to set up a project where modules along with the redux and sagas will be injected into the main app, using redux-injectors. In my sagas I want to use yield select, to check if an action has updated the state and then carry on. For example, when I post an image, I want to make sure there were no errors in posting the file and then move on. I use the following function:
export const imageErrors = (state: RootState): IImagesErrorState => state.image.errors
and then in the saga.ts file I use it as such:
if (imagesErrors?.postImageError !== null) {
throw imagesErrors.postImageError
}
this works fine as long as the state.image exists in the root state from the beginning. However, how do I do that when I want to inject this state later on using useInjectReducer and useInjectSaga? I obviously get an error
Property 'image' does not exist on type 'Reducer<CombinedState<{ user: CombinedState<{ auth: IAuthState; errors: IErrorState; }>; }>, AnyAction>'.ts(2339)
So how do we handle selectors of specific pieces of state, since state does not yet include them?
Thank you so much.
Can't talk about the Typescript part of things, but in terms of architecture you've got two options.
One is the obvious - that is to add conditions or ? everywhere to avoid errors from accessing missing properties, but that can get tedious quickly.
The other probably better option is to rethink your state & application chunks. What is this saga that is accessing state that isn't existing yet? Does it need to run before you have such state? If not, let's move the saga to the same chunk as the reducer. In the opposite case, where you need the saga to be running e.g. as part of the runtime chunk, then perhaps the image state should be in the runtime chunk as well.
I recently refactored half my app to use the provider pattern and i now have a problem. The main issue is that i need to initialise controllers in the init (e.g. a text controller to have an initial value or the list size of a tab controller)
How am i meant to init Controllers if the data i need has to come from the state in the build method.
For example.
// This must go in the build as it requires state
myTabsController = TabController(length: myState.list.length, vsync: this);
I'm initialisng the controller every time it builds now... How am i meant to put this in the init but still access the state variables (as there is no context).
I've tried using the afterFirstLayout() callback from the AfterLayoutMixin library but that just causes more problems. Currently with the tab bar it flashes error as no tab initialised for the first frame and then displays properly when the afterFirstLayout is called and initialises the tab. This seems like a hacky fix
I would like to learn more about how to use this pattern properly and what would be the best solution to this problem.
Feel free to ask me to clarify more.
Thanks for your help.
Say I have a Redux store that keeps track of an AppState that is comprised of a single 'color' variable as a string.
initialState = {
color: 'red'
}
And an Action for updating this:
const SET_COLOR = 'SET_COLOR';
function setColor(color) {
return { type: SET_COLOR, color };
}
And say I have some sort of input that allows the user to set the color to whatever they please. (How this is done is irrelevant)
let newColor = <got new color somehow>
Now lets say the user inputs 'red' (the same as the current state). Should I care if the newColor and the current color differ? Ie, should I first check the store to see if the newColor is different than the old color, and only dispatch the setColor action IFF the color is different? Or should I just dispatch the setColor action, regardless if there's a difference.
If you do it correctly (preferably using a good immutable data type for your state, e.g. immutable.js), then the new state returned by your reducer is equal to the previous state and the component will not re-render (provided you have a PureComponent or the componentShouldUpdate returns false because the state hasn't changed). So dispatching a few extra actions is practically no extra burden on your app.
In general, I would say do the easiest thing and just call setColor again. The reason is that it keeps your logic more straightforward. Any time the user changes the color via an input field, then your code will dispatch the action. Now, you only need to write test case to verify this behavior. This may sound trivial but 1) it adds up and 2) you would also have to test the case of rapidly switching back and forth between two colors to be sure your code works.
I would filter out the newColor() dispatch if there was an explicit reason to do so, such as:
Performance shows it is needed
The UI behavior needs to be different in these two scenarios to let the user know they haven't changed anything
changing the color has knock on side effects of related data that are undesirable (for example, changing the color might reset the shape to a triangle)
Or similar. The point being, do the simple thing by default unless there's a reason not to.
Redux actions are designed to be cheap. Don't be afraid of dispatching. It's a similar philosophy to React. Render, render, render, and let the framework do the heavy lifting.
I have my reducer with a starting state of an empty array:
folderReducer(state:Array<Folder> = [], action: Action)
I'd like to populate the starting state, so when I do
store.subscribe(s => ..)
The first item I get comes from the database. I assume the way of doing this is with ngrx/effects, but I'm not sure how.
Your store always has the initial state, that you define in the reducer-function. The initial states main purpose is to ensure that the application is able to start up and not run into any null-pointer-exceptions. And also it sets up your application to start making the first api-calls ect. - so you can think of it as a technical initial state.
If you want to fill your store with api-data on the startup, you would do that on the same way that you add/modify data during any other action - just that the action of "initially loading data" is not triggered by some user-interaction but through:
either when your root-component loads
or as part of a service in the constructor
In case you want to prevent specific components from showing anything until your API-call is done, you would have to adjust the display-components to display or hide data based on your state (e.g. by implementing a flag in your satet initialDataLoaded).
A dynamic initial state is now supported, see: https://github.com/ngrx/platform/blob/master/docs/store/api.md#initial-state-and-ahead-of-time-aot-compilation
Also see: https://github.com/ngrx/platform/issues/51
I would only do this if the database is local, otherwise the request will hold up loading of the application.
The store has a method called getState that will return the current state of the store.
What prevents code somewhere in my application from (accidentally) modifying the returned state from store?
Let's say i call this:
let state = store.getState();
state.someProperty = 'fun';
The implementation that i've found on getState on the store object simply returns the inner state object that gets overwritten with each new action.
const getState = () => state;
In between actions/new states what prevents code from modifying the state that will be read by another subscriber? In my above example, setting someProperty to 'fun' will persist inside the store on the state property, until overwritten.
While i'm obviously not supposed to modify the state, a simple mistake might bind the state to some component that (unknowingly) modifies its inputs - perhaps on a 2-way binding in an angular environment?
<app-some-component [user]="state"></app-some-component>
Shouldn't getState() be implemented as a clone of its state model?
P.S. This is not specifically related to Angular - which is why i didn't add the tag - to allow more people not used to Angular to answer the question.
The answer is: nothing :)
The core Redux library itself technically doesn't actually care if state gets mutated or not. You could actually mutate in your reducers, or have other parts of your app get the state tree and mutate it, and the store itself wouldn't know or care.
However, mutation will break time-travel debugging, as well as make tests unreliable. Even more importantly, the React-Redux library assumes that you will handle your state immutably, and relies on shallow equality comparisons to see if the state has changed. (This is the reason why "Why isn't my component re-rendering?" is in the Redux FAQ. 99.9% of the time, it's due to accidental mutation.)
If you are concerned about mutation, you can use a library like Immutable.js instead of plain JS objects, or use one of the several tools for freezing your state in development to catch mutations.