You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React Native Redux菜单Reducer获数据后变为Undefined求助

React Native Redux持久化问题:Menu状态填充后变为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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.28 08:30:59