与世界上所有应用程序一样,我的React应用程序需要执行对API的一些Ajax调用。
我选择使用Redux中间件,以便正确地从组件中提取API。
这个想法是从我的组件中分发REQUEST
个动作。中间件侦听它们并调度SUCCESS
或ERROR
动作:这些最后的动作由reducer监听。
这里很多人已经问过如何从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)
。 这意味着我的减速器在REQUEST
或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
用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);
};
说实话,我不太喜欢这两种解决方案,它们感觉很笨拙...
这是我的问题:我应该如何处理我的问题? 有更清洁的方法吗?
预先感谢:)
答案 0 :(得分:3)
似乎您要尝试的操作类似于Redux-Saga,我建议您看一下它们的库。
根据他们的示例
fetch(your_url)
.then(resp => resp.blob())
.then(doSomethingWithBlob)
答案 1 :(得分: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非常相似,但更简单易懂。