最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - How do I chain actions with Redux Promise Middleware? - Stack Overflow

programmeradmin1浏览0评论

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 ponent 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

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 ponent 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
Share Improve this question edited Aug 24, 2018 at 15:52 Patrick Burtchaell 4,1321 gold badge16 silver badges25 bronze badges asked Aug 23, 2018 at 13:57 SmajlSmajl 8,01532 gold badges113 silver badges182 bronze badges
Add a ment  | 

2 Answers 2

Reset to default 3

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()
            }));
    };
};

As mentioned in the documentation for redux-promise-middleware, The middleware can be bined 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)
);
发布评论

评论列表(0)

  1. 暂无评论