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

React useContext问题:App.tsx中AuthContext的userID始终为undefined求助

问题:App.tsx中通过useContext获取userID始终为undefined

在App.tsx中尝试通过useContext(AuthContext)获取用户ID(userID),但即使AuthContextProvider中已在登录时为userID赋值,App.tsx中拿到的userID始终是undefined。


相关代码

App.tsx

import "./shared/styles/crema.less";
import {
  AppContextProvider,
  AppLayout,
  AppLocaleProvider,
  AppThemeProvider,
  AuthRoutes,
} from "./domain";
import { BrowserRouter } from "react-router-dom";
import AuthContextProvider, { AuthContext } from "./pages/auth/context";
import { QueryClient, QueryClientProvider } from "react-query";
import { useContext, useEffect, useState } from "react";
import { Socket, io } from "socket.io-client";
import { func } from "prop-types";
const queryClient = new QueryClient();

export const newSocket: any = io("http://151.248.117.169:3001");

const App = () => {
  const authContext = useContext(AuthContext);
  console.log('userId: ', authContext, { authContext })
  const [socket, setSocket] = useState<any>();

  useEffect(() => {
    newSocket.connect();
    console.log("socket: ", newSocket)
    setSocket(newSocket);

    return () => {
      newSocket.disconnect();
    };
  }, []);


  useEffect(() => {
    socket?.emit('addNewAccount', {
      "id": 2,
      "type": 1,
    }, (val: any) => {
      console.log(val);
    });
  }, [socket]);

  return (
    <AppContextProvider>
      <AuthContextProvider>
        <QueryClientProvider client={queryClient}>
          <AppThemeProvider>
            <AppLocaleProvider>
              <BrowserRouter>
                <AuthRoutes>
                  <AppLayout />
                </AuthRoutes>
              </BrowserRouter>
            </AppLocaleProvider>
          </AppThemeProvider>
        </QueryClientProvider>
      </AuthContextProvider>
    </AppContextProvider>
  );
};

export default App;

AuthContextProvider.tsx

import React, { createContext, useState, useEffect, ReactElement } from "react";
import { AuthContextProps } from "./types";
import { useCookies } from "react-cookie";
import { ILoginDto } from "../../../shared/services/auth/dtos/login-dto";
import {
  AUTH_TOKEN,
  EXHIBITOR_LOGO,
  EXHIBITOR_NAME,
  EXHIBITOR_STAND_DESIGN,
} from "../../../shared/constants/AppConst";
import { authService } from "../../../shared/services/auth";
import MainUtils from "../../../shared/utils/main";
import { notifier } from "../../../shared/functions/notifier";
import { Navigate, useNavigate, useParams, useRoutes } from "react-router-dom";
export const AuthContext = createContext<AuthContextProps>({});

const AuthContextProvider = ({ children }: { children: ReactElement<any, any> }) => {
  const [authenticated, setAuthenticated] = useState<boolean>(false);
  const [loading, setLoading] = useState<boolean>(false);
  const [visibleLogout, setVisibleLogout] = useState<boolean>(false);
  const [responseError, setError] = useState<any>("");
  const [cookies, setCookies, removeCookies] = useCookies([AUTH_TOKEN]);
  const [cookiesEmail] = useCookies(["email"]);
  const [userId, setUserId] = useState<any>();

  const logout = async () => {
    setLoading(true);
    await authService.logout().then(async (response: any) => { });
    removeCookies(AUTH_TOKEN);
    removeCookies(EXHIBITOR_NAME);
    setVisibleLogout(false);
    setLoading(false);
  };

  const login = async (values: any) => {
    setLoading(true);

    let request: ILoginDto = {
      email: values?.email,
      password: values?.password,
      fcm_token: values.fcm_token,
    };
    await authService
      .login(request)
      .then((response: any) => {
        setUserId(response?.data?.id);
        if (!MainUtils.isEmptyValue(response?.errors)) {
          response?.errors?.map((error: string) => {
            notifier(error, "error");
          });
        }
        if (response?.data?.token) {
          setCookies(AUTH_TOKEN, `${response?.data?.token}`, {
            path: "/",
            sameSite: true,
          });
          setCookies(EXHIBITOR_NAME, `${response?.data?.name}`, {
            path: "/",
            sameSite: true,
          });
          setCookies(EXHIBITOR_LOGO, `${response?.data?.logo}`, {
            path: "/",
            sameSite: true,
          });
          setCookies(
            EXHIBITOR_STAND_DESIGN,
            `${response?.data?.stand_design}`,
            {
              path: "/",
              sameSite: true,
            }
          );
        }
      })
      .catch(({ response }) => {
        const errorMessage: string =
          response?.data?.errors?.[
          Object.keys(response?.data?.errors)[0]
          ]?.[0] || "Something went wrong, try again later!";
        notifier(errorMessage, "error");
      });
    setLoading(false);
  };


  const recoverPassword = async (values: any) => {
    setLoading(true);
    await authService
      .resetPassword({ ...values, email: cookiesEmail["email"] })
      .then((response: any) => {
        if (response?.error) {
          setError(response?.error?.message);
        }
        if (response?.data) {
          setError("");
          window.location.href = "/sign-in";
        }
      });
    setLoading(false);
  };

  const resendCode = async (values: any) => {
    setLoading(true);
    await authService.resendCode({ ...values }).then((response: any) => {
      if (response?.error) {
        setError(response?.error?.message);
      }

      if (response) {
        setError("");
        window.location.replace("/confirm-code");
      }
    });
    setLoading(false);
  };

  const confirmCode = async (values: any) => {
    setLoading(true);
    await authService.confirmCode({ ...values }).then((response: any) => {
      if (response?.error) {
        setError(response?.error?.message);
      }
      setCookies("email", `${values?.email}`, {
        path: "/",
        sameSite: true,
      });
      if (response?.data) {
        setError("");
        <Navigate to={"forget-password"} replace />;
      }
    });
    setLoading(false);
  };

  return (
    <AuthContext.Provider
      value={{
        responseError: responseError,
        loading: loading,
        visibleLogout: visibleLogout,
        setVisibleLogout: setVisibleLogout,
        logout: logout,
        login: login,
        recoverPassword: recoverPassword,
        isAuthenticated: authenticated,
        setAuthenticated: setAuthenticated,
        confirmCode: confirmCode,
        userId: userId,
        resendCode,
      }}
    >
      {children}
    </AuthContext.Provider>
  );
};

export default AuthContextProvider;

解决方案

1. 修复组件层级问题

当前App组件是AuthContextProvider的父组件,而useContext只能在Provider的子组件中获取到正确的上下文值。需要调整结构,让调用useContext的代码处于AuthContextProvider的包裹范围内。

修改后的App.tsx示例:

// 抽离App的核心内容到子组件中
const AppContent = () => {
  const authContext = useContext(AuthContext);
  console.log('userId: ', authContext.userId);
  const [socket, setSocket] = useState<any>();

  useEffect(() => {
    newSocket.connect();
    console.log("socket: ", newSocket)
    setSocket(newSocket);

    return () => {
      newSocket.disconnect();
    };
  }, []);

  useEffect(() => {
    socket?.emit('addNewAccount', {
      "id": authContext.userId,
      "type": 1,
    }, (val: any) => {
      console.log(val);
    });
  }, [socket, authContext.userId]);

  return (
    <AppContextProvider>
      <QueryClientProvider client={queryClient}>
        <AppThemeProvider>
          <AppLocaleProvider>
            <BrowserRouter>
              <AuthRoutes>
                <AppLayout />
              </AuthRoutes>
            </BrowserRouter>
          </AppLocaleProvider>
        </AppThemeProvider>
      </QueryClientProvider>
    </AppContextProvider>
  );
};

// 让App作为外层包裹AuthContextProvider
const App = () => {
  return (
    <AuthContextProvider>
      <AppContent />
    </AuthContextProvider>
  );
};

export default App;

2. 完善上下文默认值

创建AuthContext时的默认值为空对象,初始渲染时会导致userId为undefined。可以给默认值补充userId的初始定义:

export const AuthContext = createContext<AuthContextProps>({
  responseError: "",
  loading: false,
  visibleLogout: false,
  setVisibleLogout: () => {},
  logout: async () => {},
  login: async () => {},
  recoverPassword: async () => {},
  isAuthenticated: false,
  setAuthenticated: () => {},
  confirmCode: async () => {},
  userId: undefined,
  resendCode: async () => {},
});

3. 验证登录返回值有效性

在login函数中添加日志,确认response?.data?.id是否存在有效数值,避免因接口返回数据异常导致userId未赋值:

await authService.login(request).then((response: any) => {
  console.log('登录接口返回的用户ID:', response?.data?.id);
  if (response?.data?.id) {
    setUserId(response.data.id);
  }
  // 其他原有逻辑...
});

4. 可选:持久化userID

如果需要页面刷新后保留userID,可以在登录时将userId存入cookie,然后在AuthContextProvider初始化时读取:

// AuthContextProvider.tsx中添加userId的cookie操作
const [cookies, setCookies] = useCookies([AUTH_TOKEN, "userId"]);

useEffect(() => {
  // 初始化时从cookie读取userId
  if (cookies["userId"]) {
    setUserId(cookies["userId"]);
  }
}, []);

// login函数中存入userId到cookie
setCookies("userId", `${response?.data?.id}`, { path: "/", sameSite: true });

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.25 21:24:55