For example, if I have a login reducer and a user icon reducer. When I login, I want to update the user icon as well as storing the user's info. I am stuck between two choices:
First one is to export the USER_LOGIN action and have both login reducer and user icon reducer handle USER_LOGIN action.
Second approach is to have a 1 to 1 mapping between action and reducer (one type of action belongs to only one reducer). We have login reducer handle USER_LOGIN, then with Saga/Thunk we dispatch a side effect UPDATE_USER_ICON to the user icon reducer.
Which one is a better practice ? I personally favor the second approach.
Quoting the Redux FAQ entry on dispatching multiple actions:
There's no specific rule for how you should structure your actions. Using an async middleware like Redux Thunk certainly enables scenarios such as dispatching multiple distinct but related actions in a row, dispatching actions to represent progression of an AJAX request, dispatching actions conditionally based on state, or even dispatching an action and checking the updated state immediately afterwards.
In general, ask if these actions are related but independent, or should actually be represented as one action. Do what makes sense for your own situation but try to balance the readability of reducers with readability of the action log. For example, an action that includes the whole new state tree would make your reducer a one-liner, but the downside is now you have no history of why the changes are happening, so debugging gets really difficult. On the other hand, if you emit actions in a loop to keep them granular, it's a sign that you might want to introduce a new action type that is handled in a different way.
Try to avoid dispatching several times synchronously in a row in the places where you're concerned about performance. There are a number of addons and approaches that can batch up dispatches as well.
I would say your first approach is better and that is the one I use commonly. Yes it may affect readability of your code when you multiple reducers are acting upon same action type but it helps keep code less verbose (which is my major complain with React ecosystem) and a bit of performance. Actions like login would not have huge impact on performance but when I am making API calls on user actions, I just handle same action type in multiple reducers. For readability, I add comments and documentation.
Do you really need to store the user icon in Redux? A better approach in this case is to have a selector that returns the icon based on the logged in user. That way you can keep the minimal amount of state in the store.
You can use the Reselect library to cache selectors.
Related
I have a big component, I store all the information about the selected items and all the information in the store(ngrx).
There are different types in this component and I can switch between them and change information about them(via inputs). And then, at the click of a save button, send all changes to the server.
What is the best way to put changed data in the store?
Send to the store during an onchange event (minus: a lot of dispatch)
Send when switching between types (minus: you need to somehow check the status of the type that has been changed right now before pressing the save button)
Or, in general, is it a bad practice to put elements that will change, and better store them to service?
And another question, is it correct to carry out calculations in a reducer. For example, I dispatch a list of elements in the store and I need to add a new element for the selected type. I can do this through the service, but then I need to pull out all the elements, the selected type and a few more parameters, and then perform actions and put the changed array back into the store. Or do all these actions in a reducer with known data.
Or in general, is this the wrong architectural approach to keep this list in the store?
Without knowing the use case it's hard to give a correct answer to this question, because it all depends on the needs.
As a rule of thumb, if the state only affects the current component, the ngrx store is not the place to store its data. An example of this is a form, it's usually an overkill to sync the form with the state in the store. That being said, if you need rehydration on the form, it's a good use case to keep the form and the store in sync.
The minus of dispatching a lot of actions, isn't a "real" downside of it imho - the ngrx store (redux in general) is designed to handle a lot of incoming actions.
To answer the second question, yes that's were reducers are for imho - it's here where I expect some logic. See the redux docs for more info.
You can also put some "view logic" inside the selectors, like filtering, sorting, paginination, ...
Mike and Brandon gave a talk at ng-conf and they explain what should belong in state and what does not. The talk gives useful insights, Reducing the Boilerplate with NgRx
I am trying to build a new react app and I have some question about the architecture.
I have 2 components:
Adress
CreditCard
Each Component has several inputs and api functions to get the data and also to update it.
When I use both components in one Page, how can I trigger an update for both components?
Page
Address
CreditCard
Submit Button
The Submit Button should trigger the Update function and wait until both are finished.
I tried to give a function from the Page to the child components via props, but I think this is the wrong way.
Redux
For complex interactions between different components you can use redux. Redux holds the state of your entire application and can only be updated with actions.
An action will trigger a reducer. A reducer returns a new version of the state with what ever update you set there.
Treat redux some what like a database, do not store things multiple times and keep a good separation of concerns. There's an awesome library that lets you aggregate and manipulate data called reselect.
Reselect
This library will let you take several parts of the store and combine them for your needs in any given scenario.
Conclusion
To conclude, these's libraries are part of a stack I've been using for about 2.5 years (reselect only 1 year). I've found them very powerful for handling complex data. That being said there are other options like graphql, apollo or relay.
I would like to understand when it is appropriate to use a redux middleware over a redux-observable epic (and vice versa).
From the redux middleware documentation
It provides a third-party extension point between dispatching an action, and the moment it reaches the reducer. People use Redux middleware for logging, crash reporting, talking to an asynchronous API, routing, and more.
From the redux-observable documentation
While you'll most commonly produce actions out in response to some action you received in, that's not actually a requirement! Once you're inside your Epic, use any Observable patterns you desire as long as anything output from the final, returned stream, is an action.
My understanding is that an action in a redux middleware may or may not hit a reducer and if it does, it may or may not result in a change of state. In an epic you can produce a new action which may or may not hit a reducer or result in a change of state.
It looks like both can be used to enable side effects so the difference between the two is becoming a bit blurry to me.
Question: Is the FRP foundation the only thing that sets them apart or are there specific events in the lifecycle of an application that are better dealt with a classic middleware or an epic?
"Middleware" are a general type of thing that can be used to customize a Redux store. redux-observable is a specific Redux middleware, which lets you use RxJS logic to interact with dispatched actions.
Following the flux concepts we can get the next assertions for which I couldn't find explanations.
Every store will receive every action.
Why? My suggestion: since a store contents some business-logic we have to provide it with all possible changes and data so the store can decide what to do with them on its own.
The data in a store must only be mutated by responding to an action.
Why? My suggestion: the reason is violation of unidirectional data-flow in case of not responding to an action.
Every time a store's data changes it must emit a "change" event.
Why? I can't get this point.
Flux is just a way of managing the data flow of your application, so it is up to the developer to make sure this actually happens. But I'll try to paint a picture of why these concepts are a part of Flux.
Every store will receive every action.
If you have only one dispatcher in your application, every store will listen to actions dispatched through that dispatcher. It is up to you whether or not the store should act on the action dispatched, but to be able to react on it the store has to know of it.
Not all actions should lead to changes in a store, though. But the dispatcher simply doesn't care, because it won't know anything about the store implementation. It's just telling all stores that this action happened, do what you want with it or go on with your life without caring.
The data in a store must only be mutated by responding to an action.
You're right that doing it with a different approach could be violation of unidirectional data-flow. Doing things this way makes sure all parts of your application has the correct state based on the actions that happens.
By not doing it this way you would let go of one of the flux strengths. Update your store based on dispatched actions, and other stores will also be aware that the action happened, and thereby react to it if they want to. If you update the store directly you will end up having no clear picture of what parts of your application that are altering the state of your store.
Every time a store's data changes it must emit a "change" event.
People often describe the stores in a flux application as the source of truth. When a store's data changes, the basis for the visualization of your data changes. You want to be confident that if my store holds a certain value, this is what my application uses as it's data.
It's related to the first quote here. The store doesn't know if a listener depends on it's data. By emitting a change, it will let all listeners know that hey, I changed. Make sure you have all my latest changes. If you don't emit change, the listener could end up displaying something based on old data.
All of these statements are related to the same thing: If an action happens in your application, don't make any assumptions about which part of your application that wants to know the details of it. Make sure everyone can act on it, if they want to.
The general problem: Let's say I have a button with an onClick handler calling an action creator. The action does an ajax call which dispatches a message when ajax responds, and this in some way affects the UI. Given this basic pattern there's nothing stopping the user from clicking this button multiple times, and thus running the ajax call multiple times.
This is something that doesn't seem to be touched upon in the React or Flux documentation (as far as I have seen), so I've tried to come up with some methods on my own.
Here are those methods
Use lodash.throttle on a method which does an ajax call so that multiple clicks in quick succession don't create multiple calls.
Use lodash.debounce on a method so that ajax is only called once a user hasn't done any activity for a bit. This is how I'm doing semi-realtime updates of text fields on change.
Dispatch an "is updating" message to stores when the action is first called and then dispatch a "done" message when the ajax call returns. Do stuff like disabling input on the initial message and then re-enable on the second.
The third method seems to be the best in terms of functionality since it allows you to make the user interface reflect exactly what's going on, but it's also incredibly verbose. It clutters absolutely everything up with tons of extra state, handler methods, etc...
I don't feel like any of these methods are really idiomatic. What is?
Hal is pretty much correct. Dispatching multiple messages is the Fluxiest way to go.
However, I would be wary of dispatching an IS_UPDATING message. This makes reasoning about your code harder because for each AJAX action you're dispatching several actions at once.
The idiomatic solution is to split your AJAX "actions" (action-creator-actions) into three dispatched actions: MY_ACTION, MY_ACTION_SUCCESS, MY_ACTION_FAILURE, handling each instance appropriately, and tracking "pending-ness" along the way.
For example:
// MyActionCreator.js
// because this is in a closure, you can even use the promise
// or whatever you want as a sort of "ID" to handle multiple
// requests at one time.
postMessage() {
dispatch('POST_MESSAGE', { ... } );
api.slowMessagePostingAjaxThingy().then(
(success) => { dispatch('POST_MESSAGE_SUCCESS', { ... }); },
(failure) => { dispatch('POST_MESSAGE_FAILURE', { ... }); }
);
}
// MyStore.js
on('POST_MESSAGE', (payload) => { /* do stuff */ });
on('POST_MESSAGE_SUCCESS', (payload) => { /* handle success */ });
on('POST_MESSAGE_FAILURE', (payload) => { /* handle failure */ });
This gives you several benefits over your alternate solutions:
Your store is exclusively in control of whether an item is pending or not. You don't have to worry about changing UI state on actions in your UI code: you can have your UI look exclusively to a pending property of your store for truth. This is probably the biggest reason for using Flux over MVC systems.
You have a clean interface for taking your actions. It's easy to reason about and easy to attach other stores to this data (if you have a LatestMessageStore or something, it's easy to subscribe to these events). This is the benefit over using IS_UPDATING as Hal suggested.
You save your lodash calls for when they semantically make sense— like when you may be inundated with legitimate data (a text field).
You can easily switch between optimistic updates (change the store when POST_MESSAGE is called) or pessimistic updates (change the store on POST_MESSAGE_SUCCESS).
I would argue that the third method is the correct way, but I don't find it to be verbose. A lot of React code that I see written sort of misses the spirit of React with its idea of very small, composable components. When large monolithic components are created, yes, things can get very messy.
But if the button in question is its own component, then it can take care of rendering based on its state. When a user clicks the button, the state of just that component changes -- and it renders it in a way that it can't be clicked again.
Once the store has notified that component that it has changed, the component can set its state back -- and with it, re-render itself.
It's a pretty straight-forward process; it just requires thinking about pages as a collection of small, composable units.