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

React/React Native中AuthContext无法更新问题求助

React Native AuthContext状态无法更新问题排查与修复

在React/React Native开发中遇到AuthContext无法更新的问题,添加日志后仍无法明确user状态无法正确设置的原因,相关代码如下:

App.tsx

export default function App() {
  const { user, setUser } = useAuth();

  console.log(user)  // 这里user始终为null

  return (
    <NavigationContainer>
      <AuthContext.Provider value={{ user, setUser }}>
        <Stack.Navigator screenOptions={{ headerShown: false }}>
          {user?.session_token ? (
            <>
              <Stack.Screen
                name="BottomTabNavigator"
                component={BottomTabNavigator}
              />
            </>
          ) : (
            <>
              <Stack.Screen name="SignIn" component={SignInScreen} />
            </>
          )}
        </Stack.Navigator>
      </AuthContext.Provider>
    </NavigationContainer>
  );
}

useAuth.tsx

import { useContext, useEffect } from "react";
import { useSecureStore } from "./useSecureStore";
import { AuthContext } from "../context/authContext";

export const useAuth = () => {
  const { user, setUser } = useContext(AuthContext);
  const { getItem, setItem, removeItem } = useSecureStore();

  useEffect(() => {
    const getUser = async () => {
      const user = await getItem("user");
      if (user) {
        setUser(JSON.parse(user));
      }
    };

    getUser();
  }, []);

  const login = (user: AuthUser) => {
    setUser(user);
    setItem("user", JSON.stringify(user));
  };

  const logout = () => {
    setUser(null);
    removeItem("user");
  };

  return { user, setUser, login, logout };
};

useSecureStore.tsx

import { useState } from "react";
import * as SecureStore from "expo-secure-store";

export const useSecureStore = () => {
  const [value, setValue] = useState<string | null>(null);

  const setItem = async (key: string, value: string) => {
    await SecureStore.setItemAsync(key, value);
    setValue(value);
  };

  const getItem = async (key: string) => {
    const value = await SecureStore.getItemAsync(key);
    setValue(value);
    return value;
  };

  const removeItem = async (key: string) => {
    await SecureStore.deleteItemAsync(key);
    setValue(null);
  };

  return { value, setItem, getItem, removeItem };
};

authContext.ts

import { createContext } from "react";

interface AuthContext {
  user: AuthUser | null;
  setUser: (user: AuthUser | null) => void;
}

export const AuthContext = createContext<AuthContext>({
  user: null,
  setUser: () => {},
});

问题根源分析

  1. 上下文消费与Provider顺序错误:App.tsx中在AuthContext.Provider渲染前调用useAuth(),此时useContext拿到的是createContext的默认值(user为null、setUser为空函数),后续Provider的上下文无法覆盖该值,导致user始终为null。
  2. useAuth逻辑定位错误:useAuth作为消费上下文的hook,却把用户初始化逻辑放在其中,且在Provider外部调用,导致状态更新无法传递到正确的上下文链路。
  3. useSecureStore冗余状态:内部维护的value状态完全多余,该hook仅需提供SecureStore操作能力,同步状态会引发不必要的重渲染。

修复方案

1. 重构AuthContext,在Provider内部维护状态

修改authContext.ts,将状态管理与Provider整合:

import { createContext, useState, useEffect, ReactNode } from "react";
import * as SecureStore from "expo-secure-store";

interface AuthUser {
  session_token: string;
  // 其他用户字段
}

interface AuthContextType {
  user: AuthUser | null;
  login: (user: AuthUser) => Promise<void>;
  logout: () => Promise<void>;
}

export const AuthContext = createContext<AuthContextType>({
  user: null,
  login: async () => {},
  logout: async () => {},
});

export const AuthProvider = ({ children }: { children: ReactNode }) => {
  const [user, setUser] = useState<AuthUser | null>(null);

  // 初始化加载本地存储的用户
  useEffect(() => {
    const loadUser = async () => {
      const storedUser = await SecureStore.getItemAsync("user");
      if (storedUser) {
        setUser(JSON.parse(storedUser));
      }
    };
    loadUser();
  }, []);

  const login = async (userData: AuthUser) => {
    setUser(userData);
    await SecureStore.setItemAsync("user", JSON.stringify(userData));
  };

  const logout = async () => {
    setUser(null);
    await SecureStore.deleteItemAsync("user");
  };

  return (
    <AuthContext.Provider value={{ user, login, logout }}>
      {children}
    </AuthContext.Provider>
  );
};

2. 简化useAuth,仅作为上下文消费入口

修改useAuth.tsx,确保仅在Provider内部使用:

import { useContext } from "react";
import { AuthContext } from "../context/authContext";

export const useAuth = () => {
  const context = useContext(AuthContext);
  if (!context) {
    throw new Error("useAuth must be used within an AuthProvider");
  }
  return context;
};

3. 调整App.tsx结构,确保Provider包裹所有需要上下文的组件

import { AuthProvider } from "./context/authContext";
import { useAuth } from "./hooks/useAuth";

export default function App() {
  return (
    <AuthProvider>
      <NavigationContainer>
        <AppContent />
      </NavigationContainer>
    </AuthProvider>
  );
}

// 拆分内容组件,在Provider内部消费上下文
const AppContent = () => {
  const { user } = useAuth();

  console.log("当前用户状态:", user);

  return (
    <Stack.Navigator screenOptions={{ headerShown: false }}>
      {user?.session_token ? (
        <Stack.Screen
          name="BottomTabNavigator"
          component={BottomTabNavigator}
        />
      ) : (
        <Stack.Screen name="SignIn" component={SignInScreen} />
      )}
    </Stack.Navigator>
  );
};

4. 简化useSecureStore(可选)

若其他地方需要使用,可简化为无状态版本:

import * as SecureStore from "expo-secure-store";

export const useSecureStore = () => {
  const setItem = async (key: string, value: string) => {
    await SecureStore.setItemAsync(key, value);
  };

  const getItem = async (key: string) => {
    return await SecureStore.getItemAsync(key);
  };

  const removeItem = async (key: string) => {
    await SecureStore.deleteItemAsync(key);
  };

  return { setItem, getItem, removeItem };
};

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.18 03:07:21