React Redux 持久存储创建失败:Saga内调用IsUserLoggedIn action报错求助
问题定位与解决方案
核心报错原因
- Saga无限循环触发
你的isUserLoggedInSaga被注册为CHECK_AUTHORIZATION类型action的触发回调,但你在这个saga内部put(IsUserLoggedIn(data))生成的action,类型正好就是CHECK_AUTHORIZATION。这会形成无限循环:action不断被生成 → 不断被saga捕获 → 再次执行saga生成新的action。短时间内大量重复的状态写入,会让Redux Persist频繁触发本地存储操作,很快超出浏览器存储配额、或者因写入频率过高被浏览器拦截,直接抛出持久化存储创建失败的报错。
你替换成yield put(logOutSuccess())能正常运行,就是因为这个action的类型是LOGOUT_SUCCESS,不在saga的监听列表里,不会触发新的循环。 - 状态更新逻辑无效
你当前Reducer里针对CHECK_AUTHORIZATION的分支直接写死isLoggedIn: false,就算saga接口拿到的真实登录状态是已登录,这个值也不会被更新到全局状态里,和你写这段逻辑的初衷不符。 - 可能的导入遗漏
如果你在Saga文件中没有正确导入IsUserLoggedIn方法,直接调用时会因为引用undefined抛出报错,而logOutSuccess因为已经正确导入所以可以正常执行。
具体修改步骤
1. 调整Action层,拆分触发类型与状态更新类型
不要用同一个action类型同时承担「触发saga接口请求」和「通知reducer更新状态」两件事,新增独立的状态更新类型,从根源上切断循环链:
export const actionTypes = { LOGIN_REQUEST: 'LOGIN_REQUEST', LOGIN_SUCCESS: 'LOGIN_SUCCESS', LOGOUT: 'LOGOUT', LOGOUT_SUCCESS: 'LOGOUT_SUCCESS', CHECK_AUTHORIZATION: 'CHECK_AUTHORIZATION', // 新增:专门用于通知reducer更新登录状态的类型,避免和saga监听类型重名 SET_AUTH_STATUS: 'SET_AUTH_STATUS' }; // 保留原有触发saga的action不变 export function IsUserLoggedIn(payload) { return { type: actionTypes.CHECK_AUTHORIZATION, payload, }; } // 新增:saga拿到接口数据后,用于通知reducer更新状态的action export function setAuthStatus(payload) { return { type: actionTypes.SET_AUTH_STATUS, payload } } export function login() { return { type: actionTypes.LOGIN_REQUEST }; } export function loginSuccess() { return { type: actionTypes.LOGIN_SUCCESS }; } export function logOutSuccess() { return { type: actionTypes.LOGOUT_SUCCESS }; }
2. 调整Reducer层,修正状态更新逻辑
将原来写死状态的分支替换为新的类型,直接使用接口返回的真实数据更新状态:
import { actionTypes } from './action'; export const initState = { isLoggedIn: false, }; function reducer(state = initState, action) { switch (action.type) { case actionTypes.LOGOUT_SUCCESS: return { ...state, isLoggedIn: false, }; // 替换原来的CHECK_AUTHORIZATION分支为新的SET_AUTH_STATUS分支 case actionTypes.SET_AUTH_STATUS: return { ...state, isLoggedIn: action.payload?.isLoggedIn || false, }; default: return state; } } export default reducer;
3. 调整Saga层,切断循环触发链
将saga内部put的动作替换为新的、不会被saga监听的类型,同时优化rootSaga的写法,合并重复的all调用:
// 注意导入新增的setAuthStatus方法 import { setAuthStatus } from './action'; function* isUserLoggedInSaga({ payload }) { try { const data = yield call(AuthRepository.isUserLoggedIn, payload); // 原来这里put的IsUserLoggedIn会触发新的CHECK_AUTHORIZATION动作,替换成setAuthStatus yield put(setAuthStatus(data)); } catch (err) { console.log(err); } } // 合并重复的all调用,统一注册所有saga export default function* rootSaga() { yield all([ takeEvery(actionTypes.LOGOUT, logOutSaga), takeEvery(actionTypes.CHECK_AUTHORIZATION, isUserLoggedInSaga) ]); }
内容的提问来源于stack exchange,提问作者Sona Shetty
相关产品推荐
相关产品推荐

