登录重定向状态未同步求助:如何确保Store状态更新后再跳转
问题原因分析
你的问题核心是登录状态更新与页面重定向存在竞争关系,导致部分场景下重定向到目标页时,Redux Store的登录状态还未完成更新,甚至被后续的fetch操作覆盖:
- 登录完成后,
LoginPage在render中检测到isLoggedIn为true就立即触发重定向,但此时authService.loginOauth可能还未完成token的持久化存储。 - 重定向触发
history.listen,进而调用auth/fetch操作,此时getSignToken拿到的仍是旧的匿名token,解析失败后会注册匿名用户,覆盖刚登录成功的用户状态。 fetcheffect初始错误地将IsLoggingInOrOut设为false,导致页面无法正确识别加载状态,提前执行重定向逻辑。
具体解决方案
1. 由登录Effect控制重定向时机
将重定向逻辑从LoginPage的render移到loginOauth effect中,确保状态更新、token存储完成后再跳转:
修改models/auth.ts中的loginOauth effect:
*loginOauth({ payload }: { payload: authService.LoginOauthPayload }, { call, put, select }: any) { // 标记登录中 yield put({ type: 'setIsLoggingInOrOut', payload: { IsLoggingInOrOut: true }, }); try { // 等待登录接口完成,确保token已持久化 const user = yield call(authService.loginOauth, payload); // 更新用户状态 yield put({ type: 'save', payload: { user }, }); yield put({ type: 'clearError' }); // 获取跳转目标并执行重定向 const next = yield select((state: any) => state.auth.next); if (next) { yield put(push(next)); // 清空跳转目标 yield put({ type: 'redirect', payload: { next: undefined } }); } } catch (e) { yield put({ type: 'update', payload: { ssoLoginError: '登录失败,请重试' }, }); } finally { yield put({ type: 'setIsLoggingInOrOut', payload: { IsLoggingInOrOut: false }, }); } }
简化LoginPage/index.tsx的render逻辑:
render() { if (this.props.IsLoggingInOrOut) { return <div>登录中...</div>; } if (this.props.isLoggedIn) { return <></>; } else { return <div>The sign in page</div>; } }
2. 优化fetch逻辑,避免覆盖已登录状态
修改services/auth.ts的fetch方法,当已有有效登录用户时,不执行匿名注册:
export async function fetch(existingUser?: any): Promise<any> { const token = await getSignToken(); let user; try { user = payloadFromToken(token); if (user.exp && Date.now() > user.exp * 1000) { throw new Error('token has expired'); } } catch { // 若已有非匿名用户,保留现有状态,不注册匿名 if (existingUser && existingUser.type !== UserType.ANONYM) { return existingUser; } user = await registerAnonym(); } if (!user) { user = await logout(); } return user; }
同步修改models/auth.ts的fetch effect,传入现有用户并判断是否需要更新状态:
*fetch({ }, { call, put, take, select, all }: any) { yield put({ type: 'setIsLoggingInOrOut', payload: { IsLoggingInOrOut: true }, }); try { const existingUser = yield select((state: any) => state.auth.user); const routing = yield select(({ routing }: any) => routing); let user; if (routing === undefined) { const [u, _] = yield all([call(authService.fetch, existingUser), take(LOCATION_CHANGE)]); user = u; } else { user = yield call(authService.fetch, existingUser); } // 仅当新用户为非匿名,或现有用户为匿名时才更新状态 const shouldUpdate = !existingUser || existingUser.type === UserType.ANONYM || user.type !== UserType.ANONYM; if (shouldUpdate) { yield put({ type: 'save', payload: { user }, }); } yield put({ type: 'clearError' }); } catch (e) { console.error('获取用户信息失败:', e); } finally { yield put({ type: 'setIsLoggingInOrOut', payload: { IsLoggingInOrOut: false }, }); } }
3. 确保登录时token持久化完成
检查authService.loginOauth的实现,必须等待token存储完成后再返回用户信息:
export async function loginOauth(payload: LoginOauthPayload): Promise<User> { const response = await api.post('/auth/oauth', payload); const { token, user } = response.data; // 等待token写入本地存储完成 await setSignToken(token); return user; }
内容的提问来源于stack exchange,提问作者SoftTimur
相关产品推荐
相关产品推荐

