Reputation: 7995
I am quite new to React and Redux. I want to chain multiple API calls using redux-promise-middleware and implemented my actions as follows:
locationActions.js:
import { visitLocation } from '../services/actionService';
export const VISIT_LOCATION = 'VISIT_LOCATION';
export const VISIT_LOCATION_PENDING = 'VISIT_LOCATION_PENDING';
export const VISIT_LOCATION_FULFILLED = 'VISIT_LOCATION_FULFILLED';
export const VISIT_LOCATION_REJECTED = 'VISIT_LOCATION_REJECTED';
const visitLocationAction = (characterId, location) => ({
type: VISIT_LOCATION,
// visitLocation returns a new promise
payload: visitLocation(characterId, location)
});
export { visitLocationAction as visitLocation };
I dispatch this action from my React component using mapDispatchToProps
:
const mapDispatchToProps = dispatch => (
bindActionCreators({ visitLocation }, dispatch)
);
This works! However, I want to dispatch another action after visitLocation
is settled and fulfilled. I can not call Promise.then()
because it doesn't provide the dispatch
method, therefore not "binding" my action to the reducer.
Tutorials mention I should call dispatch(secondAction())
but I do not have dispatch available in my action creators.
Can someone point out to me what am I missing?
Edit:
As suggested in the first answer, I tried the following approach:
import { visitLocation } from '../services/locationService';
import { fetchSomething } from '../services/otherService';
const visitLocationAction = (characterId, location) => {
return (dispatch) => {
return dispatch(visitLocation(characterId, location))
.then(() => dispatch(fetchSomething()));
};
};
export { visitLocationAction as visitLocation };
But I got action:undefined and the following error:
Error: Actions must be plain objects. Use custom middleware for async actions.
at dispatch (redux.js:200)
at redux-logger.js:1
Upvotes: 1
Views: 7327
Reputation: 4132
Tutorials mention I should call
dispatch(secondAction())
but I do not have dispatch available in my action creators.
Let me answer this part of your question first. As mentioned in the first answer, you need Redux Thunk, a middleware, to use dispatch
in your action creators.
By default, Redux action creators return plain objects like this:
const returnsAnObject = () => ({
type: 'MY_ACTION'
payload: ...,
meta: ....
})
With Redux Thunk, action creators can essentially return functions. A function that returns a function is called a "thunk", hence the name of Redux Thunk.
const returnsAFunction = (dispatch) => dispatch({
type: 'MY_ACTION'
payload: ...,
meta: ....
})
In the specific case of Redux Thunk, you return the dispatch
function. Redux Thunk enables you to return dispatch
by providing it as a parameter to your action creators.
But I got
action:undefined
Let's unpack this. We know--thanks to Redux Thunk--you can chain multiple actions together using dispatch
. However, as you mentioned in your edited question, you still get the "plain object" error.
Error: Actions must be plain objects. Use custom middleware for async actions.
at dispatch (redux.js:200)
at redux-logger.js:1
This is because you called dispatch
with a Promise object, not a plain object.
const visitLocationAction = (characterId, location) => {
return (dispatch) => {
return dispatch(visitLocation(characterId, location))
// Yes, it is correct to call dispatch here
// However, `dispatch` accepts plain objects, not Promise objects
.then(() => dispatch(fetchSomething()));
};
};
To fix this problem, simply modify your second dispatch to use a plain object:
const visitLocationAction = (characterId, location) => {
return (dispatch) => {
return dispatch(visitLocation(characterId, location))
.then(() => dispatch({
type: 'VISIT_LOCATION_SECOND_TIME'
payload: fetchSomething()
}));
};
};
Upvotes: 3
Reputation: 1101
As mentioned in the documentation for redux-promise-middleware, The middleware can be combined with Redux Thunk to chain action creators.
So you can write your action like:
const secondAction = (data) => ({
type: 'SECOND',
payload: {...},
})
const thirdAction = (data) => ({
type: 'THIRD',
payload: {...},
})
const firstAction = () => {
return (dispatch) => {
return visitLocation(characterId, location)
.then((data) => dispatch(secondAction(data)))
.then((data) => dispatch(thirdAction(data)))
}
}
or use async/await
const secondAction = (data) => ({
type: 'SECOND',
payload: {...},
})
const thirdAction = (data) => ({
type: 'THIRD',
payload: {...},
})
const firstAction = async () => {
return (dispatch) => {
const data = await visitLocation(characterId, location));
dispatch(secondAction(data))
dispatch(thirdAction(data))
}
}
and then use it as you mentioned:
const mapDispatchToProps = dispatch => (
bindActionCreators({ firstAction }, dispatch)
);
Upvotes: 1