Redux: async and sync dispatches - reactjs

I have a multi-page user registration form in my react/redux app.
When a user clicks "next page," it dispatches a call to update the user's profile in the local redux store (not the server).
When they click "Submit" on the final page, I currently have a click handler along the lines of:
clickHandler: function(formData) {
dispatch(updateProfileInStore(formData);
dispatch(saveProfileToServer());
}
My question is: can I be 100% certain that the Redux store w/ my user profile will update before the second dispatch is called?
I'm worried that in some cases the profile might be written to my database before the last page of form data is added to it.

It's not guaranteed. What you could do is pass saveProfileToServer the new form data have it update state and send the updated form data to the server.
As per the react documentation
setState() does not immediately mutate this.state but creates a
pending state transition. ... There is no
guarantee of synchronous operation of calls to setState and calls may
be batched for performance gains.

The only way to add a synchronous behaviour here is to either use redux thunk or sagas. A redux thunk returns promise that resolves and in sagas, we may have to watch for success action dispatched by updateProfileInStore which needs to call saveProfileToServer.

Related

Update redux state outside React component

I have a react project configured with redux for state management.
For api calls I use a file called axiosInstance. In this file I need to have access to redux store (and I have access importing store and using getState - store.getState()). Now the problem is that I want also to update the redux state from this file (axiosInstance).
How I can update redux store from this file (which is not a react component) in a efficient method?
Okay, I assume that you are using Axios for the network calls.
tl;dr
Use store.dispatch(action) and action to make changes to store state
Yes, the store can be accessed with store.getState(), you can also change state with store.dispatch(action). as per the docs
Dispatches an action. This is the only way to trigger a state change.
The store's reducing function will be called with the current getState() result and the given action synchronously. Its return value will be considered the next state. It will be returned from getState() from now on, and the change listeners will immediately be notified.
This happens because of the functional paradigm that redux follows (google for more).
Now, the action has to be defined with an actionCreator, or simply you could call .dispatch() with an inline object with one string property as {type: 'name-of-action'}. This prompts the store to change the state.
You may require redux-thunk for async actions.
Conclusion:
Let's save some trouble here as you may have many calls to the store, you can create a context that handles the AxiosInstance (as there is already a different file for that).
Make a context in the AxiosInstance file and then you can start dispatching actions as per the network responses. this will save you a lot of trouble as the state is updated by redux without actually looking at it much.

How to keep updated react context value inside a callback in a custom hook

I have a userContext wrapped my app, I have a custom hook useAnalytics used to send analytics events and I have a Login component which performs the log in and changes the user value in the context and send an event.
My problem is when I do a setUser inside my Login component the updating user data is not reflected in useAnalytics hook which it sends always the user data in every event
Here a sandbox: https://codesandbox.io/s/romantic-perlman-wzxti?file=/src/Login.js
You can open the console and see the results after clicking SEND
I know this is probably an anti-pattern but it would be good to know why I get this behavior
Thanks!
The problem is that all set states are async, while the sending of the event is in sync. So you dispatch the change, send the analytic and after a timeout, the state is updated.
There are 2 main ways to fix this.
Set a timeout to wait for react to update the state and push the analytic after the state is updated:
setUser({ type: "login", data: { user: "pippo" } });
setTimeout(sendEvent, 16);
Send the analytic with a middleware:
Add a middleware to intercept all reducer changes and push those to the analytic without the hook itself. This will guarantee that you get all changes that you need to track by checking for the action.type and sending the new data from action.payload.

Making API Calls with React for fetch data and render it

I am new to React and want to understand the difference from classic MVC.
I want to create a simple components that loads some data initially and renders let say a grid.
On some state or prop change it will reload the data and re-render.
What is the best approach in react from below two options?
using the lifecycle events to load the data, update some state and render while in another event will show some loading opacity.
Work with redux and react-redux? but in all example I cant see API calls.
Is this the role of a middleware (Thunk?)?
Will appropriate an explanation.
Both the approaches are correct. It depends on your use case. If you can avoid using redux in your app, use the lifecycle methods to make API calls (also called subscriptions in react documentation). If you think your app has many components and different components needs to share a state, then use redux.
You should also look at React hooks https://reactjs.org/docs/hooks-reference.html
You can use Effect Hook https://reactjs.org/docs/hooks-effect.html to make API calls and update your component's state.
Update:
Both Thunk and Sage are used to manage side effects in your application (making API calls from here). I've used saga, I don't know much about thunk.
How you would use redux-saga:
Say if you want to get some data for display on a button click, this is how it works:
On button click you dispatch an action, say GET_DATA
Your redux reducer will change some state on this particular action, say isLoading=true
You can use isLoading in your component to show a spinner/overlay
At the same time saga will listen to GET_DATA action and make the API call
If success, from Saga you'll dispatch an action, say GET_DATA_SUCCESS with the data from API call
Reducer will change isLoading=false and set the data in state, say apiData = { ... }
If failure, from Saga you'll dispatch an action, say GET_DATA_FAILED with the error code/message
Reducer will change isLoading=false and set the error in state, say apiError = { ... }
You can now use isLoading=false in you component to remove spinner and display the data (apiData) or error (apiError) in you component.
You can go through this tutorial to learn more https://redux-saga.js.org/docs/introduction/BeginnerTutorial.html

Initialising State in React Redux app with data in server

I am working on my first React Redux project.
The project depends on a server to get its data and therefore there is also a server API that needs to be called in order to get those data.
My question is about initialising state.
There are 2 ways I can think of initialising state:
1.) First pass an empty object {} as the initialised state of the Redux store then inside componentDidMount that is where I call the API to access the data in the server and then update the state of the store/app then.
2.) In the reducer of the Redux app call all relevant server method (e.g. getCategories(), getPosts(), etc.) then compose a object with all of the data e.g. {categories: categories, posts: posts} then pass this object to the reducer as the initial state.
Which way is the recommended way for Redux when dealing with data stored in the server?
If there is a 3rd or 4th way that is the Redux way or the recommended way then please share your knowledge. Thank you.
The way I work with data coming from the back end, and as far as I know is also the suggested way, is this:
In the componentDidMount method call a thunk action creator. you can read more about them in this link.
Inside that thunk action creator you first dispatch a REQUEST action, then perform a fetch to access the back end and when the response arrives you handle it on either the success or error handlers. Depending on the handler executed you dispatch either a SUCCESS or an ERROR action (and you attach to it all the relevant information that is coming from the back end so the reducer can access it).
In your reducer you write code to handle all the above actions (REQUEST, SUCCESS and ERROR). Each handler will transition your state, for instance the REQUEST can set an isFetching flag to true that will let you show a spinner in the UI, and the SUCCESS can set that flag to false and populate the state with the data coming from the back end and passed to the reducer using the action dispatched.
Once your reducer is updated, you will access that updated state from the UI, for instance using the connect react-redux function.
Regarding the initial state, it should represent a default state. for instance, it will have the isFetching flag in false and, if you are fetching a list of foods from the back end, then that list could be an empty list in your initial state. This is just a for instance of course. You need to set an initial state that makes sense to your app.
I hope this helps you.
The first way is the better way of initializing state. Your component makes the necessary API calls in componentDidMount and passes the data to Redux as payload of actions which the reducers use to update the state of your application.
The second way is not advisable. According to the Redux docs:
The reducer is a pure function that takes the previous state and an action, and returns the next state.
(previousState, action) => newState
...
It's very important that the reducer stays pure. Things you should never do inside a reducer:
Mutate its arguments;
Perform side effects like API calls and routing transitions;
Call non-pure functions, e.g. Date.now() or Math.random().
Edit:
You can also use thunk middleware and async actions to do API calls as explained in the Redux docs and #DiegoTArg's answer.

Fluxxor - how to dispatch an action in response to another?

I feel like subtitling this question "Does Fluxxor want my application to work?". No doubt however I have not grasped how to co-operate with Fluxxor...
The error is:
Cannot dispatch an action ('NEXT_AJAX_REQUEST_IS_ABOUT_TO_BE_MADE') while another action ('FIRST_AJAX_REQUEST_IS_RETURNED') is being dispatched
Component A is the owner of Component B
Component A calls an action creator in its componentWillReceiveProps. This action creator makes an AJAX request. If successful we dispatch FIRST_AJAX_REQUEST_IS_RETURNED with some data into a store. The store updates. It emits change. Component A is listening for this change. Component A updates its state. Component A re-renders passing this state as props to Component B. Component B in its componentWillReceiveProps calls an action creator to make a second AJAX request based on the data it now has. Before the actual AJAX request is made the action creator dispatches NEXT_AJAX_REQUEST_IS_ABOUT_TO_BE_MADE - in order to put a store into a certain state.
I get the message about dispatching one action while another is in progress it seems because Fluxxor does not consider the first action complete - even though I cannot have made the second until the AJAX request was completed and I have the data. The issue seems to be calling the second action from the componentWillReceiveProps method of a child component in response to changes in the owner component's state arising from the first action.
So. I tried moving the second action to the success 'callback' in the first store. I still get the same message. Even if this was the right thing to do...
The second action depends on data contained in the first action. I cannot merge these two into one single request/action and then update 2 stores using waitFor.
http://fluxxor.com/guides/react.html#batched-updates
This fixed it.
However I do not understand why and would really appreciate an explanation if anyone has it.
I think that the effect is that Component A will not re-render until state changes arising from getStateFromFlux have fully completed. So that when Component B calls its action creator we are now fully done with the first action.
Is this approximately correct?

Resources