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
相关产品推荐
相关产品推荐

