React Native Redux菜单Reducer获数据后变为Undefined求助
问题描述
现有auth和menu两个reducer,auth可正常持久化数据,但menu存在异常:调用getMenu() action后,menu状态初始能正确填充数据,但随即变为undefined;Expo重载应用后再次调用可获取数据,但仍会再次变为undefined,需实现菜单数据的全应用持久化。
问题排查
1. AsyncThunk与extraReducers的逻辑冲突
menuAction.js中getMenu这个createAsyncThunk的回调函数无返回值,但menuReducer.js的extraReducers监听了menu/getMenu/fulfilled事件,此时action.payload为undefined,会覆盖之前通过dispatch(setMenu(menu))设置的正确菜单数据,导致状态变为undefined。
同时,thunk内部手动dispatch了setLoading、setMenu等action,又通过extraReducers监听thunk的生命周期事件更新状态,属于重复更新逻辑,引发状态覆盖。
2. Redux Persist未配置Menu持久化
store.js中rootPersistConfig的whitelist为空数组,意味着没有任何reducer被持久化,即使菜单状态正常,重载应用后也会丢失数据。
解决方案
步骤1:修复AsyncThunk的重复更新逻辑
方案A:移除extraReducers的监听,由thunk内部dispatch处理状态
修改menuReducer.js,删除extraReducers部分:
import { createSlice } from "@reduxjs/toolkit"; const initialState = { menu: null, loading: false, error: null, }; const menuSlice = createSlice({ name: "menu", initialState, reducers: { setLoading: (state, action) => { state.loading = action.payload; }, setError: (state, action) => { state.error = action.payload; state.loading = false; }, setMenu: (state, action) => { state.menu = action.payload; state.loading = false; }, clearMenu: (state) => { state.menu = null; }, clearError: (state) => { state.error = null; }, }, // 移除extraReducers代码块 }); export const { setLoading, setError, setMenu, clearMenu, clearError } = menuSlice.actions; export default menuSlice.reducer;
方案B:由extraReducers处理状态,移除thunk内部的dispatch
修改menuAction.js,让thunk返回数据并抛出错误:
export const getMenu = createAsyncThunk( "menu/getMenu", async ({ storeNumber, token }) => { try { const menu = await GetMenuApi(storeNumber, token); return menu; // 返回数据作为fulfilled的payload } catch (error) { throw error; // 抛出错误作为rejected的payload } } );
修改menuReducer.js,保留extraReducers并直接引用thunk的case:
import { createSlice } from "@reduxjs/toolkit"; import { getMenu } from "./menuAction"; // 导入getMenu thunk const initialState = { menu: null, loading: false, error: null, }; const menuSlice = createSlice({ name: "menu", initialState, reducers: { clearMenu: (state) => { state.menu = null; }, }, extraReducers: (builder) => { builder .addCase(getMenu.pending, (state) => { state.loading = true; state.error = null; }) .addCase(getMenu.fulfilled, (state, action) => { state.loading = false; state.menu = action.payload; }) .addCase(getMenu.rejected, (state, action) => { state.loading = false; state.error = action.error.message; }); }, }); export const { clearMenu } = menuSlice.actions; export default menuSlice.reducer;
步骤2:配置Redux Persist持久化Menu状态
修改store.js,将menu加入持久化白名单,并移除重复的combineReducers导入:
import { configureStore, combineReducers } from "@reduxjs/toolkit"; import { persistStore, persistReducer } from "redux-persist"; import AsyncStorage from "@react-native-async-storage/async-storage"; import authReducer from "./auth/authReducer"; import menuReducer from "./menu/menuReducer"; const rootPersistConfig = { key: "root", storage: AsyncStorage, keyPrefix: "redux-", whitelist: ["auth", "menu"], // 添加需要持久化的reducer }; const rootReducer = combineReducers({ auth: authReducer, menu: menuReducer, }); const persistedReducer = persistReducer(rootPersistConfig, rootReducer); export const store = configureStore({ middleware: (getDefaultMiddleware) => getDefaultMiddleware({ serializableCheck: false, }), reducer: persistedReducer, }); export const persistor = persistStore(store);
步骤3:验证效果
重载Expo应用,点击按钮调用getMenuFunc获取菜单数据,之后重启应用,检查useSelector获取的menu状态是否保留,且不会变为undefined。
内容的提问来源于stack exchange,提问作者Brian

