如何以正确的顺序从Redux中间件调度操作

问题描述 投票:2回答:2

作为世界上应用程序的必备品,我的React应用程序需要对API执行一些Ajax调用。

我选择使用Redux中间件来正确地将API提取与我的组件分开。 我的想法是从我的组件发送REQUEST操作。中间件听取它们并发送SUCCESSERROR行动:这些最后由减速器监听。

这里有很多人已经问过如何从Redux中间件调度操作:这不是我的问题的主题:)

首先,让我向您展示一个我用来编写的简单减速器:

function currentUserReduxer(state = {}, action = {}) {
  const { currentUser, error } = action.payload;

  switch (action.type) {
    case 'GET_CURRENT_USER_REQUEST':
      return { ...state, isFetching: true, error: null };

    case 'GET_CURRENT_USER_SUCCESS':
      return { ...state, id: currentUser.id, isFetching: false };

    case 'GET_CURRENT_USER_FAILURE':
      return { ...state, isFetching: false, error };

    default:
      return state;
  }
}

和相应的中间件:

() => next => async (action) => {
  next(action);

  switch (action.type) {
    case'GET_CURRENT_USER_REQUEST': {
      try {
        const currentUser = await api.getCurrentUser();
        next(actions.getCurrentUserSuccess(currentUser));
      } catch (error) {
        next(actions.getCurrentUserFailure(error));
      }
      break;
    }

    default:
      break;
  }
};

它很长一段时间以来运行良好,然后我意识到它部分错误:我的中间件没有返回next的值,所以它打破了中间件链,这是错误的! 由于next(action);是我在中间件中执行的第一件事,我很快就无法返回它,所以我在中间件的末尾移动了它。我还决定发布新动作而不是使用next(毕竟,它们是新行动,将它们发送到整个中间件链是有意义的)。我的新中间件现在看起来像这样:

store => next => async (action) => {
  switch (action.type) {
    case'GET_CURRENT_USER_REQUEST': {
      try {
        const currentUser = await api.getCurrentUser();
        store.dispatch(actions.getCurrentUserSuccess(currentUser));
      } catch (error) {
        store.dispatch(actions.getCurrentUserFailure(error));
      }
      break;
    }

    default:
      break;
  }

  return next(action);
};

它看起来很棒,但我现在有另一个问题:因为store.dispatch是同步的,所以next(action)被称为。这意味着我的减速器在REQUESTSUCCESS之后接受FAILURE动作:(

我认为一个解决方案可能是使用旧的承诺而不是await

store => next => async (action) => {
  switch (action.type) {
    case'GET_CURRENT_USER_REQUEST': {
      api.getCurrentUser()
        .then((currentUser) => {
          store.dispatch(actions.getCurrentUserSuccess(currentUser));
        })
        .catch((error) => {
          store.dispatch(actions.getCurrentUserFailure(error));
        });
      break;
    }

    default:
      break;
  }

  return next(action);
};

另一个想法是用store.dispatch包装setTimeout

store => next => async (action) => {
  switch (action.type) {
    case'GET_CURRENT_USER_REQUEST': {
      try {
        const currentUser = await api.getCurrentUser();

        setTimeout(() => store.dispatch(actions.getCurrentUserSuccess(currentUser)));
      } catch (error) {
        setTimeout(() => store.dispatch(actions.getCurrentUserFailure(error)));
      }
      break;
    }

    default:
      break;
  }

  return next(action);
};

说实话,我真的不喜欢这两种解决方案,他们觉得这么hacky ......

所以这是我的问题:我怎么想处理我的问题?有更干净的方法吗?

提前致谢 :)

javascript redux dispatch-async redux-middleware
2个回答
3
投票

看起来像你想要做的是类似于Redux-Saga我建议你看看他们的图书馆。

取自他们的榜样

// worker Saga: will be fired on USER_FETCH_REQUESTED actions
function* fetchUser(action) {
   try {
      const user = yield call(Api.fetchUser, action.payload.userId);
      yield put({type: "USER_FETCH_SUCCEEDED", user: user});
   } catch (e) {
      yield put({type: "USER_FETCH_FAILED", message: e.message});
   }
}

/*
  Starts fetchUser on each dispatched `USER_FETCH_REQUESTED` action.
  Allows concurrent fetches of user.
*/
function* mySaga() {
  yield takeEvery("USER_FETCH_REQUESTED", fetchUser);
}

0
投票

我认为你的第一个解决方案是接近干净的答案。您可以引入术语sideEffects,它是在您的操作发送时执行的异步函数。您的中间件功能仍然是同步的,操作将立即发送。这是一个例子:

getCurrentUserSideEffects = async (action) => {
    api.getCurrentUser()
        .then((currentUser) => {
          store.dispatch(actions.getCurrentUserSuccess(currentUser));
        })
        .catch((error) => {
          store.dispatch(actions.getCurrentUserFailure(error));
        });
}

<other side effects>

store => next => action => {
  switch (action.type) {
    case 'GET_CURRENT_USER_REQUEST': {
      getCurrentUserSideEffects(action);
      break;
    }

    default:
      break;
  }

  return next(action);
};

我认为这个想法与redux saga非常相似,但更简单,更容易理解。

© www.soinside.com 2019 - 2024. All rights reserved.