redux-saga中await generator执行完成后仍获取旧authorizedError值问题
问题根源
- 你写的
await dispatch(xxx)没有实际生效:默认Redux环境下dispatch普通action返回值是action对象本身,不是Promise,await操作不会等待saga的异步逻辑执行完成,你的判断代码会在登录请求发出后立刻执行,此时saga还未更新authorizedError状态。 - 闭包快照问题:
onSignInPress函数由useCallback生成,内部捕获的authorizedError是当前渲染周期的状态值,就算后续Redux状态更新,当前闭包里的authorizedError还是旧值,不会实时刷新。
解决方案
方案1:通过useEffect监听状态变化(最推荐,符合React单向数据流)
把错误判断逻辑从点击回调里移到单独的useEffect中,专门监听authorizedError的变化触发提示,同时记得新增重置错误状态的action避免重复触发提示。
改造后的组件代码:
const authorizedError = useSelector((state: RootState) => state.user.authorizedError); // 监听错误状态变化,触发提示 useEffect(() => { if (authorizedError) { setNotificationErrors(['Wrong login or password']); showNotification(); // 触发提示后重置错误状态,需要在userActions里新增resetSignInError action dispatch(userActions.resetSignInError()); } }, [authorizedError, dispatch, setNotificationErrors, showNotification]); const onSignInPress = useCallback(() => { // 只需要触发登录请求即可,不需要等待和判断 dispatch(userActions.postLoginUser({username: email, password})); }, [validate, email, password, dispatch]);
同时需要在你的用户状态对应的reducer里新增resetSignInError的处理逻辑,将authorizedError重置为false即可。
方案2:改造dispatch支持返回Promise(适合需要在回调内直接拿到请求结果的场景)
默认Redux不支持dispatch返回Promise,你可以给登录action挂载resolve/reject回调,在saga执行完成后调用回调返回结果,改造逻辑如下:
// 组件内点击回调改造 const onSignInPress = useCallback(async () => { new Promise((resolve, reject) => { dispatch(userActions.postLoginUser({ payload: {username: email, password}, resolve, reject })) }).then(() => { // 登录成功后续逻辑 }).catch(() => { // 登录失败逻辑 setNotificationErrors(['Wrong login or password']); showNotification(); }) }, [validate, email, password, dispatch]);
对应saga代码改造:
export default function* postLoginUserSaga({ payload, resolve, reject }: PayloadAction<{payload: UserCredentialsPayload, resolve: Function, reject: Function}>) { try { yield put(setSignInError(false)); const { data: { payload: { access_token }, status, }, } = yield transport.post(URLS.postLoginUserURL, payload); if (status !== "Ok") { throw new Error(status); } yield setItemAsync(ACCESS_TOKEN_KEY, access_token); yield put(setSignIn(true)); resolve?.(); } catch (error) { console.error("User login failed", error); yield put(setSignInError(true)); reject?.(error); } }
内容的提问来源于stack exchange,提问作者Igor
相关产品推荐
相关产品推荐

