React/Redux how to wait for dispatched action to complete? - reactjs

In my thunk, I have two dispatches in an array called dispatches which modify the Redux store. Only after both of them have completed (i.e., modified the Redux store), I want to dispatch finalDispatchCall. I tried putting them both in a Promise and called then, but I still see the finalDispatchCall being made before the secondDispatchCall has updated the Redux store.
const dispatches = [];
dispatches.push(dispatch(firstDispatchCall());
dispatches.push(dispatch(secondDispatchCall());
Promise.all([...dispatches]).then(() => {
dispatch(finalDispatchCall());
})
.catch(error => {
logger.error(
`Print out error - ${error}`
);
});
Is there a way to make sure a dispatch has completed before calling another dispatch?
EDIT (more details):
The firstDispatchCall is making an API call (returning fetch) and dispatching an action in the then statement to update the redux store. The secondDispatchCall is also making an API call (returning fetch) and dispatching two other actions in their then statements which each make their own API calls and dispatch actions to update the redux store.
I want to wait until all of this is complete before making that finalDispatchCall.

As long as firstDispatchCall() and secondDispatchCall() are sync, then redux guarantees that they'll arrive synchronously, in sequence, and that subsequent calls to dispatch() will act upon the updated state. But, if you want to be absolutely certain, you can use store.subscribe(), to kind of procedurally await all the changes you're expecting, and read the store on each change to confirm they're all there. This will actually be essential if first and second DispatchCall are async.
But, the question is whether you really need to know, from that specific thunk, when all the dispatches have landed. Redux is an event bus, and as such offers many ways to skin the async cat. It might help to understand more about the problem you're trying to solve by waiting for these dispatches in order to dispatch the last one. It may be that you don't really need to do that...
I recommend reading the Redux team's take on this exact question: "Should I dispatch multiple actions in a row from one action creator?". Especially note there:
"In general, ask if these actions are related but independent, or
should actually be represented as one action."
Newcomers to Redux often use multiple actions for what could be a single action, because they forget that multiple reducers can all listen to the same action.

Related

When using React / Redux, how is dispatching an action that merely changes the Redux state vs actually doing some task?

For the official Redux async app, when a user wants to see Reddit topic of "reactjs", "frontend" or "reduxjs", the dispatch merely dispatches an action object:
{
type: SELECT_SUBREDDIT,
subreddit: 'frontend'
}
and then let the componentDidUpdate(prevProps) to handles the "side effect" of fetching the data.
I would have done it by not dispatching the action, but dispatching a function:
dispatch(
dispatch => {
fetch(`https://www.reddit.com/r/${topic}.json`)
.then(response => response.json())
.then(data => {
dispatch({
type: "GOT_DATA",
data: data
})
});
}
)
I thought my approach is more straight-forward, and if using componentDidUpdate() to do the side effect, won't the component be rendered three time? First for the change of topic, the second time for dispatch of a fetching state (and with the code to fetch the data), and the third time when data comes back.
This approach actually is quite interesting: it is "just to change the state of the app and I won't do anything" to begin with, and then in the class component's lifecycle code (or function component's useEffect()), it is, "when the props that come in (from state), says something needs to be done, I will do it, and change more state if needed."
Also, the first way of doing things, if we look at the source code, has so many states and conditions. This is just a simple example of fetching some Reddit topics. What if there are 7 states and conditions, then the code will be so full of conditions handling of this state is true, and that state is not, and the third state is true, etc? It can be 2⁷ combinations and the code is so tightly coupled -- if the manager asks some behavior to be changed, it is like the programmer has to re-wire everything and check for all conditions, all actions, all dispatch, etc. It is like a spaghetti of states and conditions.
I thought the original intention of having this MVC or component way of doing things is so that we have less imperative code, but now we actually may end up with more code (about 275 lines of code), and they imperatively dispatch actions, and reduce, and handle all conditions, and it is even more complicated than if we do it imperatively.
Sure, it shows the "Loading..." status, and can cache the data, and can allow refresh... but even if this is done by imperative code, it may be actually shorter code, and it is more direct and can be more easily understood.
I'd hope to know what methods there can be: do we have to, or in best practice, follow the way in the original example code, or can we do it the second method as above -- is it actually not a good practice to dispatch a function that does async or "side effect" processing?
Both your approach and theirs involve dispatching a function, to be handled by redux-thunk. The part that makes this a bit more interesting is that there are two things that need to be done:
Immediately update which subreddit is marked as selected (this value is used on app.js line 42)
Download data for that subreddit
There's a few ways this could be done. Option 1: dispatch an action to update the selected subredit, then separately dispatch an action to do the fetch. Something like:
handleChange = (nextSubreddit) => {
this.props.dispatch(selectSubreddit(nextSubreddit));
this.props.dispatch(fetchPostsIfNeeded(nextSubreddit));
}
This works, but it has a vulnerability. What if i dispatch one, but forget to dispatch the other? Then i'll have the app in an inconsistent state. Perhaps not a big problem if this is the only place in the code where we're doing this, but redux's benefit comes in complicated applications where multiple parts of the application may be interacting with the state, and they need to not mess eachother up. So for a more complicated app i wouldn't want to leave it up to the discipline of the developer to know that dispatching one should always be followed by dispatching another.
Option 2: Put the dispatching of the select action inside the thunk
export const fetchPostsIfNeeded = subreddit => (dispatch, getState) => {
dispatch(selectSubreddit(subreddit);
if (shouldFetchPosts(getState(), subreddit)) {
return dispatch(fetchPosts(subreddit))
}
}
// ... elsewhere:
handleChange = (nextSubreddit) => {
this.props.dispatch(fetchPostsIfNeeded(nextSubreddit));
}
This approach is an improvement in the sense that now there's a single point of entry, which will make sure both things happen. But it's still actually possible to dispatch selectSubreddit on its own. We've made a way to dispatch both at once, but the old way still exists.
Option 3: Use a change in state as the indicator to fetch data.
This is the one in their code. If the state is changed for any reason, the component can see that change and make the appropriate action. This further improves upon option two in terms of making sure that changing the state can't be done in isolation.
One downside of this is that the component needs to be mounted in order to kick off the fetch. That's not often a problem, since if it isn't mounted why would you need the data anyway? But it could be an issue if there are multiple components that want to consume the data, since you may be forced to duplicate the fetching logic in all the components. If that's a concern i'd probably fall back to option 2.
One thing i havn't touched on in these possibilities is the number of renders. And that's because it's no different in any of them. Regardless, you're going to have an initial render, then a synchronous render to change the selected subreddit, and then some time later a render to show the resulting data.

How to listen to certain action in redux

I am using React and Redux (react starter kit).
I have a set of action, and when some are called, I want to dispatch side effects.
For exemple, I have a workspace that contains projects, if workspaceID in the store change, (using my action setWorkspaceId) then, the projects store will notice the change, and start execute some api call to update itself.
IN angular/ngrx, I was using Effects, so the Effect where dispatching other action when a certain action was called.
What is the redux way ? I was checking into Redux-Saga, but is there something else or already built in ?
Left alone, Redux just accumulates state from the actions it receives via dispatch, so there is no single Redux-blessed way to trigger side effects from actions.
But it supports middleware, which allows you to monitor, subscribe to and even change actions that are dispatched.
If you're familiar with RxJS via ngrx, redux-observable might be something worth looking into. It allows you to consume and emit actions via RxJS's observable type. The use case you describe of triggering an API call when a value changes would be well-suited to this.
But as you say, there's also redux-saga, which uses generators to model asynchronous logic around the consumption and production of Redux actions and other side effects. There are pros and cons to these and all the other solutions out there.
Ultimately I suggest starting with what's most familiar to you, especially if you're new to Redux.
One piece of unsolicited advice - middleware is easy to overuse. Be wary of doing in middleware and side effects that which can be achieved in a reducer.
It's easy and tempting enough to set up middleware to dispatch action B in response to action A, but if all action B does is update some state, maybe that state's reducer can just react to action A.
What is the redux way?
Use mapStateToProps to subscribe for store changes.
Then use useEffect in your function component, with workspaceID as dependency, to do some side effects (i.e calling an api).
function Component(props) {
useEffect(
() => {
// call api
}
, [props.workspaceID] // only run effect when workspaceID changes
)
}
mapStateToProps(state) {
return {
workspaceID: state.workspaceID // subscribe to workspaceID changes in store
}
}
connect(mapStateToProps, {})(Component)

Redux actions vs redux-saga actions

I am kind of in dilemma when do we need to use redux actions and redux saga actions.
I don't mean to ask when to use redux and when to use redux-saga. But I would like to know that what scenario do we need to use redux actions when we use redux saga. Or don't I need to use redux actions when using redux saga?
Please help me understanding the use cases of redux when using redux-saga.
Not sure if this is what you meant, but just in case: the actions themselves are indistinguishable between redux and redux-saga. In both cases, an action is an object with a type, and possibly a payload. For example, this is an action:
{
type: 'DO_STUFF',
value: 3,
}
When you dispatch an action, it's first going to go into your middlewares. Redux-saga is an example of a middleware, and it can decide whether it wants to do anything with the action. If the action gets past redux-saga, it then goes to the reducers.
So i believe what you're asking is: when should you handle an action with a saga, and when should you handle it with a reducer. The answer to that comes down to the reason redux-saga and similar middleware were created in the first place:
Reducers are always synchronous pure functions.
So you'll use a saga when you want to do something asynchronous and/or impure. The asynchronous part of that is the thing that comes up most often. If you want to fetch data, it will be in a saga. If you want to set up an interval to do something over time, it will be in a saga.
On rare occasions you might have something that's synchronous, but impure, and that might also be something you put in a saga. One recent example i had was that i wanted to dispatch an action, and rather than updating the application state, i wanted it to save something to local storage. This happens synchronously, but since it's a side effect it's better for me to put it in a saga.
As requested in the comments, a clarification about synchronous but impure. A function is synchronous if it does all its work before it returns. A function is pure if it 1) has no side effects and 2) for a given input, it always returns the same output.
A side effect is when a function changes something external to itself, but by a means other than its return value. With a pure function, i should be able to call it and assume that nothing has changed outside of that function. For example:
localStorage.setItem('data', 'hello world');
doSomething();
console.log(localStorage.getItem('data'));
If i know nothing about doSomething except that it's pure, then i can be certain that localStorage.getItem('data') will return 'hello world'. But on the other hand if doSomething is impure, then i can't make that assumption. In principle, the value in local storage could have been changed, resulting in a different log statement.
So a function that modifies local storage, such as the following, is impure despite being synchronous:
const doSomething = (value) => {
localStorage.setItem('data', value);
}
The second way something could be impure is if it doesn't always return the same thing for its same inputs. For example, the following function is synchronous but impure:
const getTime = () => {
return Date.now();
}
If i call that multiple times, i will usually get different numbers each time.

React-Redux: Mutating the state to emulate action side-effects with redux-undo

I have a few fields that, when updated, change the href of an image (which is really an endpoint of an external API that generates a PNG based on text input).
When that image loads, it triggers an action to update another component to match the image size (that it can only get after the image has finished loading). I set redux-undo to exclude that action from the history since it's not a user action.
Here's my problem: When I do an action that does NOT trigger a change, then undo that action, the state is wrong since the size has changed in between user actions.
An idea I had was to mutate the state (yeesh):
In reducer
case 'UPDATE_TARGET_SIZE':
/**
* HERE BE DRAGONS
* I am also mutating the current state since this action needs to
* NOT count as an user action
*/
state.targetWidth = action.targetWidth
state.targetHeight = action.targetHeight
return {
...state
}
While it works with no apparent drawbacks... it feels like a dirty hack. Is there another way of doing so or is it safe as long as I know why I'm mutating the state?
Can a lib like redux-saga help me? I admit I have not read its documentation since I am not making "real" API calls and I don't want to have an overkill solution.
Edit :
Here's a better example:
I type in a text input, which causes the href to change. A new image is loaded, which triggers the action that is excluded from the history. At that point, the history does not have the modification of that last action.
If I do an action that copies that part of the sate (in the right reducer), the state is fine. If I do an action that touches only another reducer, it will still be wrong.
So when I press undo, I undo to the wrong state. But if I get that action to mutate the state, then it's always valid.
Can a lib like redux-saga help me? I admit I have not read its
documentation since I am not making "real" API calls and I don't want
to have an overkill solution.
Redux-saga allows to perform custom side effects and introduce process manager since it has own event loop. Applying to original task, saga can help with splitting action with request and response in time.
Typical use case is emitting action with postfix _REQUEST, like LOAD_IMAGE_REQUEST, which intercepts with saga manager and does not pass into reducers. Then after async operation is done, saga emits _SUCCESS or _FAILURE-like action, dependent on sucessfullity of operation. That actions passed into store and applied with reducers.
Also, there is ideology called optimistic update. In that case _REQUEST event passed into store, but if action is failed, saga sents compensating event, which rollback optimistic action

How to dispatch multiple actions one after another

In my react / redux application I often want to dispatch multiple actions after another.
Given the following example: After a successful login I want to store the user data and after that I want to initiate another async action that loads application configuration from the server.
I know that I can use redux-thunkto build an action creator like this
function loginRequestSuccess(data) {
return function(dispatch) {
dispatch(saveUserData(data.user))
dispatch(loadConfig())
}
}
So my questions are:
When the first dispatchreturns, is the state already changed by all reducers listening for that action? I'm wondering if the two dispatch calls are run strictly sequential.
Is this approach considered best practice for dispatching multiple actions? If not, what would you suggest alternatively?
Thanks for any help!
Yes redux-thunk allows you to do as you say, and yes it is a best practice for dispatching multiple actions (one that I prefer over the alternatives because of it's simplicity). The state is indeed updated as soon as you dispatch (by the time it returns), which is why you are given a function to retrieve the state in a thunk, instead of simply a reference to the state. In that way you can alter the state with one dispatch, and then call getState() to get a reference to the new state to read from if you need to.
redux-thunk is exactly what you are looking for. I consider it very good practice.
To answer your question, indeed when the first dispatch returns the state is already changed by the called reducers.
I know its a late response :-P You can do this by using Redux-Thunk and also checkout Redux-Saga. Redux Saga provides an amazing way to handle loads of actions at the same time. Below is an example of dispatching multiple actions using Redux-Thunk
function getAndLoadSider(moduleName){
return function(dispatch){
return doSomeApiCall(someData).then(function(item){
let _item = someOtherFunction(item.collections);
let _menuData = {
name: item.name,
key: item.key
};
return new Promise(function(res, rej){
dispatch(actionOne(_menuData));
res(_item);
}).then((_item) => dispatch(actionTwo(_item)))
}
}
Above method works well for your case when one action is dependent on the first. This is a promise based approach. If you don't like to do a lots of Promise coding I recommend you go for Sagas. Check out this link https://redux-saga.js.org/docs/advanced/ComposingSagas.html where you will learn how to compose sagas. Learning curve is steep; but once you are done, Sagas will make you a ninja redux dev.
Hope this helps :-)

Resources