I'm building a react native app with react navigation, which means I can't pass down props to my components the usual way (they are all nested inside a <Stack.Screen> component from react navigation).
Instead, I'm using the react Context API to pass data down through my component tree.
My problem is that everytime I use dispatch, it seems to act asynchronously. If I do:
const Submit = function () {
//this line is caught by my reducer and the signUpData is added to the state
dispatch({ type: "signUp", signUpData})
axios({ url:'myapiURL', data: state.signUpData, method: "POST" })
}
My post request will be sent with undefined instead of the proper signUpData. Whatever I try to do immediately after a dispatch, it is as if the state has not been updated yet, because dispatch is async.
The problem is that dispatch is not supposed to be async (as far as i understand ? the docs are unclear) and because it doesn't return a promise, I can't use .then() or await/async to make sure the dispatch is complete before my axios or console log happens on the next line.
Related
I am trying to get my head around a scenario where I am dispatching a synchronous redux action (using createAction of typesafe-actions) and soon after that making a network call that relies on updated props from the store.
Scenario:
Inside clearFilters handler function (handler function invoked on click of clear filters button), I am dispatching a synchronous action and then making a network call as below:
clearFilters = (): void => {
this.props.resetFilters(); //action dispatched
this.refreshData; //network call
};
Inside the refreshData function, my component expects updated filter props and based on it, it creates a searchCondition to be passed to the list api call as payload.
refreshData = (): void => {
const { listData, filters } = this.props; //get the filters prop
//Expected filters to be updated from the store post dispatch of action
const SearchCondition: SearchCondition = createSearchConditions(filters);
listData({
SearchCondition,
MaxResults: this.maxRecordsCount,
SortFields: this.getSortFields(),
}),
);
};
My component is subscribed to the filters prop using mapStateToProps:
const mapStateToProps = (state: RootState) => ({
filters: state.common.filter.filters,
});
Given that is the state of the problem I am facing, I tried to debug what happens by placing debug points in the code:
When the action is dispatched (inside clearFilters function)
Inside the reducer, where updated state is returned.
When the network call is invoked (inside clearFilters function)
In the refreshData call.
After reducer returns updated state, as per the debugging knowledge, store did not send the updated props right away. Rather, the control goes back to the next line i.e. this.refreshData() which make network call with old filters data. Only after the clearFilters function call finishes, in my componentDidUpdate, i can see that props update happen.
Does that signifies redux state change back to the store and eventually subscribed prop updates happen in an ASYNC way? If so, how does it happen? Does store sending the updated props executes in the main thread?
Any pointers/documentation would be really helpful.
The dispatch is synchronous, and the queueing of the React updates is synchronous. However, React will not re-render that component until after this whole event processing is completed, and this.props will not be updated until after that render happens. So, no, you cannot access this.props right after dispatching an action and expect that it has been updated. That will never be true.
I would suggest reading these posts that go into extensive detail on both React and React-Redux:
A (Mostly) Complete Guide to React Rendering Behavior
The History and Implementation of React-Redux
I have an async function to call before render
(the async function fetches firebase RemoteConfig and I want to use the value when rendering)
Now, I have the following structure
async componentDidMount() {
await remoteConfig.fetch()
}
Problem is that the fetch call is not guaranteed to be called before render (in my test)
How do I make sure to fetch the data before render()?
if you want to execute function before render, I think there are other hooks like getDerivedStateFromProps . But calling api before render is generally not recommended. Using componenDidMount is good, you should structure you code like:
componentDidMount(async () => {const res = await fetch(//your api)})
Once you receive the data, you call this.setState to update your state and react will automatically update the UI
I am facing the problem how to refresh data with redux in my react component after an asynchronous POST-Call. I am already using a redux-middleware which manages my rest calls.
Use-Case:
Inside a react component the user creates a new element (send data asynchronous via REST-API to backend).
Inside the same react component I have a selectable list which now should also contain the new element (with generated Database-ID).
What`s the best react+redux paradigma to update entity in backend and refresh the depending list afterwards?
Best regards Sven
For async operations like making Rest calls, you need a middleware like thunk or saga to dispatch actions based on async response. This is how your create is supposed to work on a high level with a middleware:
dispatch the create action with payload
middleware will issue the rest call to create the object
Based on the response returned, the middleware will dispatch further actions to the store eg. adding the new item to the list.
Thanks to your hints I just found the answer.
Redux (with redux-thunk middleware) can handle my case just out of the box.
Just combine both actions inside one "wrapper action".
Redux will asynchronous execute the "wrapper action".
But due to my "await" statements inside the "wrapper action" my POST and GET Request will be called synchronous in the right order and each updates my redux store as needed.
See: https://github.com/reduxjs/redux-thunk
export const createEntityAndReloadEntities: ICrudPutAction<IEntityProfile> = newEntity => async dispatch => {
return Promise.all[
await dispatch({
type: "CREATE_ENTITY",
payload: axios.post(apiUrl, newEntity)
}),
await dispatch({
type: "FETCH_ENTITY_LIST",
payload: axios.get<IEntityProfile>(`${apiUrl}`)
})];
};
In the code that I used to the example I have a property form to add a new property, after it save it in the database I want to update the redux store with the new property and send user to the properties page where all properties are listed but I want to do that after dispatch(addProperty(savedProperty)) then I can do history.push("/properties"). How can I wait there until dispatch finish. I am using useDispatch from react-redux.
async function handlePropertyFormSubmit(values) {
const savedProperty = await savePropertyGraphQlPromise(values);
if (savedProperty) {
dispatch(reset("property"));//reset property form
dispatch(addProperty(savedProperty));
//here I want to wait the dispatch finish and then send user to the properties page.
history.push("/properties");
}
}
Looks like you're using async/await so you should be able to use that to await the dispatch as well.
async function handlePropertyFormSubmit(values) {
const savedProperty = await savePropertyGraphQlPromise(values);
if (savedProperty) {
await dispatch(reset("property"));//reset property form
await dispatch(addProperty(savedProperty));
history.push("/properties");
}
}
If the addProperty action returns a promise (such as a thunk), you could use the then method as well.
dispatch(addProperty(savedProperty)).then(() => {
history.push('/properties')
})
wrap you second dispatch with setTimeout
something like this
firstDispatch()
setTimeout(secondDispatch)
You can create a 'signal' with your dispatch - like dispatched: false and set it to true when you finish your dispatch (then unset it after redirected, if you intend to use this property in more components). If you're using react router, you can instead render <Redirect to="yourNewURL" /> to redirect (render it conditionally based on redux store state).
I have a state that specify which component render(component A or B).
This state is determined when my action dispatch specific type(for example GO_TO_B or GO_TO_A).
Then I need to fetch some config from server to render component A. I want these config be in store.So I should call a action(for example fetchConfig() ) to async fetch data from server and dispatch response.
My question is that where i call the fetchConfig() action.
if I call this action in componentDidMount() in component A the error occur that cannot dispatch middle of dispatch.
So which method in react life cycle call after dispatch process and before render ?
I understand you are using redux.
If that's correct, i recommend to do your fetching with a thunk.
redux-thunk is a middleware that allows you to dispatch functions (instead of serialized objetcs like actions), that way you can deley a dispatch of an action or even dispatch it conditionaly.
An example of a thunk would be like that:
function loadSomeThings() {
return dispatch => {
fetchFirstThingAsync.then(data => { // first API call
dispatch({ type: 'FIRST_THING_SUCESS', data }); // you can dispatch this action if you want to let reducers take care of the first API call
return fetchSecondThingAsync(data), // another API call with the data received from the first call that returns a promise
})
.then(data => {
dispatch({ type: 'SECOND_THING_SUCESS', data }); // the reducers will handle this one as its the object they are waiting for
});
};
}
You can notice we can even chain ajax requests and we can dispatch (if we want) an action on each success (OR FAIL!).
I recommend reading the docs to understand it better.
You can use componentWillMount : Doc.
componentWillMount() is invoked immediately before mounting occurs. It is called before render(), therefore setting state synchronously in this method will not trigger a re-rendering. Avoid introducing any side-effects or subscriptions in this method.