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

React Native中Dispatch无法触发Reducer的问题排查求助

Expo Router中React Native Context Dispatch不生效问题排查与解决

问题核心

基于Expo Router的React Native项目中,可正常访问App Context的state,但调用dispatch执行SET_ACCESS等操作时,无法进入appReducer函数,状态无法更新。

错误分析

1. 根布局(_layout.tsx)Context提供逻辑错误

根_layout.tsx中直接通过useContext(AppContext)获取默认值的state和dispatch,再重新包装成Provider。由于AppContext的默认dispatch是() => null,所有子组件拿到的都是无效dispatch,自然无法触发reducer。

2. appReducer存在逻辑缺陷

  • SET_ACCESS分支在action.payload为空时无返回值,且未添加break,会继续执行后续LOGOUT分支,导致状态异常。
  • reducer中直接调用异步函数storeData,违反reducer必须是纯函数的原则,引发不可预测的状态更新。

3. RegisterScreen中Formik按钮绑定错误

Register按钮的onPress写成() => handleSubmit,未直接调用handleSubmit,导致表单提交逻辑无法触发。

修复步骤

1. 修复根布局(_layout.tsx)

使用AppProvider直接包裹Slot,确保子组件获取到useReducer生成的有效state和dispatch:

import { Slot } from "expo-router";
import { AppProvider } from "../context/AppContext";

export default function Root() {
  return (
    <AppProvider>
      <Slot />
    </AppProvider>
  );
}

2. 修复AppContext.tsx中的reducer与异步逻辑

将reducer改为纯函数,用useEffect监听状态变化处理异步操作:

import React, {
  createContext,
  useContext,
  useReducer,
  useEffect,
  ReactNode,
  Dispatch,
} from "react";
import AsyncStorage from "@react-native-async-storage/async-storage";
import { StateType, ActionType } from "../types/contextTypes";

const storeData = async (name: string, value: string) => {
  await AsyncStorage.setItem(name, value);
};

const returnStoredData = async (name: string) => {
  const value = await AsyncStorage.getItem(name);
  return value ?? null;
};

const initialState = {
  user: null,
  access: null,
};

const appReducer = (state: StateType, action: ActionType) => {
  switch (action.type) {
    case "SET_USER": {
      return { ...state, user: action.payload };
    }
    case "SET_ACCESS": {
      return { ...state, access: action.payload };
    }
    case "LOGOUT": {
      return { ...state, access: null, refresh: null };
    }
    default:
      return state;
  }
};

export const AppContext = createContext<{
  state: StateType;
  dispatch: Dispatch<ActionType>;
}>({ state: initialState, dispatch: () => null });

export const useAppContext = () => {
  return useContext(AppContext);
};

export const AppProvider = ({ children }: { children: ReactNode }) => {
  const [state, dispatch] = useReducer(appReducer, initialState);

  // 监听access变化,异步存储到AsyncStorage
  useEffect(() => {
    if (state.access) {
      storeData("ACCESS", state.access);
    }
  }, [state.access]);

  // 初始化加载存储的access
  useEffect(() => {
    const loadStoredData = async () => {
      const storedAccess = await returnStoredData("ACCESS");
      dispatch({ type: "SET_ACCESS", payload: storedAccess });
    };
    loadStoredData();
  }, []);

  // LOGOUT时移除存储的access
  useEffect(() => {
    const clearAccessStorage = async () => {
      if (!state.access) {
        await AsyncStorage.removeItem("ACCESS");
      }
    };
    clearAccessStorage();
  }, [state.access]);

  return (
    <AppContext.Provider value={{ state, dispatch }}>
      {children}
    </AppContext.Provider>
  );
};

3. 修复RegisterScreen.tsx的Formik按钮

将Register按钮的onPress直接绑定handleSubmit:

// ... 其他代码保持不变
<Button title="Register" onPress={handleSubmit} />
// ... 其他代码保持不变

验证效果

修复完成后:

  • 调用dispatch({ type: "SET_ACCESS", payload: ... })会正常进入appReducer对应分支,更新state。
  • state.access变化时,会自动同步到AsyncStorage。
  • 表单提交逻辑可正常触发。

内容的提问来源于stack exchange,提问作者Jason

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.28 07:24:52