In flux I'm wondering, is it okay to
make async operation
change multiple values (by different keys) in state
trigger actions
in a single store? If I need to update 2 keys of store, should I create another store to separate concerns and make store responsible for a single first level property in state?
E.g. in Redux reducer is responsible for a single first level key on resulted object, asaik
Make async operations:
Typically it is better to keep your stores synchronous... they should be dumb and just receive data. Makes everything easier and testable! The action creator should dispatch the appropriate action once it has resolved.
Change multiple values (by different keys) in state:
This isn't that bad, but as you eluded too, perhaps you need to rethink how your app state is structured. It depends on the action though... hard to say without knowing the context.
Trigger actions:
Your views are responsible for triggering actions... So stores should not trigger actions!
Some links:
Async requests with React.js and Flux, revisited.
Using a Redux store in your React.js application
Related
I have two components,in the first one i have an array of objects I get by calling the API (in y useEffect, only if the array in the store is empty, to avoid unnecessary calls). In the second one, the same array but with buttons to call the API and DELETE or POST a new object to that array in the server. My question is, is it better to create 2 actions in my second component's actions, for making the api updates, and then filter or push that object in my first component's reducer? Or just make the first component to always call the API and update itself?
I don't know if it's better to update the store as mutch as u can without relying on an API call or just an API call for updating will be more smooth.
I would recommend you to update the first component always with API call. If so, you can be sure that data has updated properly in DB via service call and you will always get updated data from DB.
Doing more and more updates at UI and keeping them in store will leave you with dirty data after a while.
It makes your app buggy (You should keep reverting/clearing the data in store in failure scenarios. Which we might miss at some point of time) too.
Why reducer must return new state what is the reason for that .Why can't we return the updated state? Is that the pattern that we must follow or what?Also please let me know that ngrx and redux are they completely different?
I think because the view layer needs to compare current state and previous state, they should be different objects. Also, it can support other features like debugging, time travel.
In both the library, They return a newly modified state or the original state
Just going through the official docs of both NgRX reducer and Redux reducer
NGRX Reducer
Reducers in NgRx are responsible for handling transitions from one state to the next state in your application.
Reducer functions are pure functions in that they produce the same output for a given input. They are without side effects and handle each state transition synchronously. Each reducer function takes the latest Action dispatched, the current state, and determines whether to return a newly modified state or the original state
Redux Reducer
Reducers specify how the application's state changes in response to actions sent to the store.
Regardless of the state management pattern, You need to change the state through reducers as actions are responsible fpr source of information for the store. They are the entry points to interact with store in Both NgRx and 'redux', moreover in Vuex too.
As per the state management library implementation, I guess they both follow same principle of Actions, Reducer to update the state async. There might be some possibly they may have different feature.
Hope this helps!
Both libraries aim to manage a state which is only manipulated in particular, predefined ways; reducers are the access they provide to the state.
By limiting the ability to manipulate the state directly, they make it easier to understand how a particular state was reached; it is always possible to reach a particular state by dispatching the same actions again, and a given state can only be reached as a result of the actions dispatched to state (at least, this is the ideal - impure* reducers would potentially lead to different states being reached from the same actions).
If we imagine a state manager which allowed functions to manipulate state, which is what would be required to return a mutated version of the original state, then it would be far more difficult to understand how a given state was reached, as the store could have been manipulated at any point by any function.
This article gives a good overview of the key ideas behind redux and explains why redux does the things it does. Here are the relevant parts for your question:
State is read-only
The only way to change the state is to emit an action, an object describing what happened.
This ensures that neither the views nor the network callbacks will ever write directly to the state. Instead, they express an intent to transform the state. Because all changes are centralized and happen one by one in a strict order, there are no subtle race conditions to watch out for. As actions are just plain objects, they can be logged, serialized, stored, and later replayed for debugging or testing purposes.
Changes are made with pure functions
To specify how the state tree is transformed by actions, you write pure reducers.
Reducers are just pure functions that take the previous state and an action, and return the next state. Remember to return new state objects, instead of mutating the previous state. You can start with a single reducer, and as your app grows, split it off into smaller reducers that manage specific parts of the state tree. Because reducers are just functions, you can control the order in which they are called, pass additional data, or even make reusable reducers for common tasks such as pagination.
I have far less experience with ngrx, though as it seems like a redux-inspired store, I'll presume it follows more or less the same principles. I'd be happy to be proven wrong, in which case I can update this answer.
*An impure function function would do one or many of the following:
Access state other than the arguments it was passed
Manipulate the arguments it was passed
Contain a side effect - something which affects state outside of itself
Mutating state is the most common cause of bugs in Redux applications, including components failing to re-render properly, and will also break time-travel debugging in the Redux DevTools. Actual mutation of state values should always be avoided, both inside reducers and in all other application code.
Use tools such as redux-immutable-state-invariant to catch mutations during development, and Immer to avoid accidental mutations in state updates.
Note: it is okay to modify copies of existing values - that is a normal part of writing immutable update logic. Also, if you are using the Immer library for immutable updates, writing "mutating" logic is acceptable because the real data isn't being mutated - Immer safely tracks changes and generates immutably-updated values internally.
From Redux Doc.
From: https://redux.js.org/introduction/three-principles
State is read-only. The only way to change the state is to emit an action, an object describing what happened. This ensures that neither the views nor the network callbacks will ever write directly to the state. Instead, they express an intent to transform the state. Because all changes are centralized and happen one by one in a strict order, there are no subtle race conditions to watch out for. As actions are just plain objects, they can be logged, serialized, stored, and later replayed for debugging or testing purposes.
Two questions arise for me from this...
How is redux enforcing that changes happen in a strict order? If I change state synchronously then I don't see why this would ever be a problem. If I want to change state after async event 1 and async event 2 in order then wouldn't I have to do something like use callbacks or promises regardless of whether I'm using redux to ensure that the state changes in the order I expect?
Why is it easier to log an action? If I weren't using redux, couldn't I just console.log every state change I make to make it easier for debugging and testing? Am I just saving the time of writing console.log upon each state change by learning redux?
To answer your questions:
redux's execution is synchronous so when you dispatch an action, you're executing a a method on the redux store, which call the reducer to compute the new state. The concept of "async actions" don't exist in redux, which is why you have a host of solutions to enable them: redux-thunk, redux-saga, redux-observable and so on. All "async actions" library eventually have to execute the dispatch function synchronously to change the redux state.
In a well-written redux applications, changes to any state contained within the redux store could only have been caused by an action dispatched from somewhere within the application that has access to the dispatch function. This allows you to have total control and knowledge of where and how state changes happen. That's the main selling point of redux: "predictable state container." You can certainly store your local state in some global variable and manually mutate it but then you'll have to use something like Object.observe (which is actually deprecated with no replacement in sight) on that state variable to monitor changes to it.
I struggle finding the right way to mutate my state in an ngrx application as the state is rather complex and depending on many factors. This Question is not about doing one piece of code correct but more about how to design such a software in general, what are doe's and don'ts when finding some hacky solutions and workarounds.
The app 'evolved' by time and i wan't to share this process in an abstracted way to make my point clear:
Stage 1
State contains Entities. Those represent nodes in a tree and are linked by ids. Modifying or adding an entity requires a check about the type of nodes the new/modified ones should be connected with. Also it might be that upon modifying a node, other nodes had to be updated.
The solution was: create functions that do the job. Call them right in the reducer so everything is always up to date and synchronus when used (there are services that might modify state).
Stage 2
A configuration is added to the state having an impact on the way the automatically modifyed nodes are modifyed/created. This configuration is saved in it's own state right under the root state.
The solution:
1) Modify the actions to also take the required data from the configuration.
2) Modify the places where the actions are created/dispatched (add some ugly
this.state.select(fromRoot.getX)
.first()
.(subscribe(element => {this.state.dispatch(new Action({...old_payload, newPayload: element}))})
wrapper around the dispatch-calls)
3) modify the functions doing the node-modification and
4) adding the argument-passing to the function calls inside the reducer
Stage 3
Now i'am asked to again add another configuration to the process, also retrived by the backend and also saved in another state right under the root state
State now looks like:
root
|__nodes
|__config_1
|__config_2
i was just about to repeat the steps as in stage 2 but the actions get really ig with all the data passed in and functions have to carry around a lot of data. This seems to be wrong, when i actually dispatch the action on the state containing all the needed info.
How can i handle this correct?
Some ideasi already had:
use Effects: they are able to get everything from state they need and can create everything - so i only need to dispatch an action with only the actions payload, the effect then can grab everything from the state it needs. I don't like this idea because it triggers asynchronus tasks to modify the state and add not-state-changing actions.
use a service: with a service holding state it would be much like with effects but without using actions to just create asynchronus calls which then dispatch the actions that relly change state.
do all the stuffi n the component: at the moment the components are kept pretty simple when it comes to changing state as i prefer the idea that actions carry as little data as possible, since reducers can access the state to get theyr data - but this is where the problem occus, this time i can't get hands on the data i need.
What type of data structure does redux use for making the data persist in Angular and React.js? I am assuming it uses persistent data structures.
Redux is an architecture for managing your state. It doesn't use any data structure. It keeps the data structure which you provide to it, i.e., state. If you want to keep an eye in your store and want to go back to the actions fired then you'll have to maintain that in your state and handle that yourself. Some modules like redux state history provide that for you. You might as well want to look at implementing undo history with redux in which you'll find that nothing is persistent with redux but you'll have to make your app persistent by storing history. Redux devtools will give you the freedom of changing your state dynamically in the browser by going back in the history of actions fired.
Redux doesn't actually use any data structures itself. The "current state" value is whatever you return from your root reducer function. That could be a simple counter value, a plain JS array or object, an Immutable.js Map or List, or something else.
I'm not sure I understand fully your question but
"In computing, a persistent data structure is a data structure that always preserves the previous version of itself when it is modified"
As far as I know redux is just a pattern and there is no persistant data structures associated with, unless you create and bind something yourself (like mongo event sourcing for example).