与世界上的应用程序一样,我的 React 应用程序需要对 API 执行一些 Ajax 调用。
我选择使用 Redux 中间件,以便正确地将 API 获取与我的组件分开。这个想法是从我的组件
中调度操作。REQUEST
中间件监听它们并调度SUCCESS
或ERROR
操作:这些最后由减速器监听。
这里的很多人已经问过如何从 Redux 中间件发送操作:这不是我的问题的主题 :)
首先,让我向您展示一个我用来编写的简单 reducer:
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)
所以在之后调用。这意味着我的减速器在orREQUEST
之后收到动作SUCCESS
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
用 a包装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...
所以这是我的问题:我应该如何处理我的问题?有没有更干净的方法来做到这一点?
提前致谢 :)