我如何使用 Redux Promise 中间件链接操作?

How do I chain actions with Redux Promise Middleware?

我对 React 和 Redux 还很陌生。我想使用 redux-promise-middleware 链接多个 API 调用并按如下方式实现我的操作:

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

我使用 mapDispatchToProps:

从我的 React 组件发送这个动作
const mapDispatchToProps = dispatch => (
    bindActionCreators({ visitLocation }, dispatch)
);

这有效!但是,我想在 visitLocation 完成并完成后派发另一个动作。我不能调用 Promise.then() 因为它没有提供 dispatch 方法,因此我对减速器的操作不是 "binding"。

教程提到我应该调用 dispatch(secondAction()),但我的动作创建器中没有调度可用。

有人可以指出我遗漏了什么吗?

编辑:

按照第一个答案的建议,我尝试了以下方法:

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

但是我得到 action:undefined 和以下错误:

Error: Actions must be plain objects. Use custom middleware for async actions.
    at dispatch (redux.js:200)
    at redux-logger.js:1

redux-promise-middleware, The middleware can be combined with Redux Thunk 链接动作创建者的文档中所述。

所以你可以这样写你的动作:

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

或使用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))
      }
    }

然后像你提到的那样使用它:

const mapDispatchToProps = dispatch => (
  bindActionCreators({ firstAction }, dispatch)
);

Tutorials mention I should call dispatch(secondAction()) but I do not have dispatch available in my action creators.

我先回答你这部分问题。正如第一个答案中提到的,您需要 Redux Thunk,一个中间件,才能在您的 action creators 中使用 dispatch

默认情况下,Redux action creators return 像这样的普通对象:

const returnsAnObject = () => ({
  type: 'MY_ACTION'
  payload: ...,
  meta: ....
})

借助 Redux Thunk,动作创建者基本上可以 return 发挥作用。 return 函数的函数称为 "thunk",Redux Thunk 的名称由此而来。

const returnsAFunction = (dispatch) => dispatch({
  type: 'MY_ACTION'
  payload: ...,
  meta: ....
})

在 Redux Thunk 的特定情况下,您 return dispatch 函数。 Redux Thunk 使您能够 return dispatch 通过将其作为参数提供给您的操作创建者。

But I got action:undefined

让我们打开包装。我们知道——感谢 Redux Thunk——你可以使用 dispatch 将多个动作链接在一起。但是,正如您在编辑后的问题中提到的那样,您仍然会收到 "plain object" 错误。

Error: Actions must be plain objects. Use custom middleware for async actions.
    at dispatch (redux.js:200)
    at redux-logger.js:1

这是因为您调用 dispatch 时使用的是 Promise 对象,而不是普通对象。

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

要解决此问题,只需修改您的第二个分派以使用普通对象:

const visitLocationAction = (characterId, location) => {
    return (dispatch) => {
        return dispatch(visitLocation(characterId, location))
            .then(() => dispatch({
                type: 'VISIT_LOCATION_SECOND_TIME'
                payload: fetchSomething()
            }));
    };
};