React AuthContext中直接访问与函数获取AuthService状态结果不一致
问题解决:React AuthContext状态更新异常
问题现象
调用AuthService类的logInAsGuest函数后,直接打印authUser对象显示isLogin已变为true,但通过authUser.currentUser()获取的isLogin仍为false,同时组件不会根据状态变化重新渲染。
核心原因
- React的
useState仅在初始渲染时创建AuthService实例的引用,后续直接修改实例内部的this.state,React无法检测到引用变化,不会触发组件重新渲染。 - 类实例内部的状态修改没有同步到React的状态管理中,导致
isLogin等状态无法在组件中正确更新。
解决方案
将用户认证状态移至AuthProvider的useState中管理,让AuthService作为无状态的工具类,仅负责处理认证逻辑并触发状态更新,确保React能正确感知状态变化。
修改后的代码
1. AuthProvider.js
import React, { createContext, useState, useContext } from "react"; import AuthService from "./AuthService"; const AuthContext = React.createContext(); export function useAuth() { return useContext(AuthContext); } export function AuthProvider(props) { // 将认证状态放在React的useState中管理 const [authState, setAuthState] = useState({ admin: false, jwt: "", email: "", username: "", isLogin: false, }); // 初始化AuthService,传入状态更新函数 const authService = new AuthService(setAuthState); const value = { authState, authService, isLogin: authState.isLogin, }; return ( <AuthContext.Provider value={value}> {props.children} </AuthContext.Provider> ); }
2. AuthService.js
const axios = require("axios").default; axios.defaults.baseURL = "http://localhost:1337/api"; class AuthService { constructor(setAuthState) { // 接收React的状态更新函数 this.setAuthState = setAuthState; } LogInAsGuest() { console.log("Logging in as guest... Auth context"); // 通过setAuthState更新React状态 this.setAuthState({ admin: false, jwt: "", email: "", username: "", isLogin: true, }); } logOut() { this.setAuthState({ admin: false, jwt: "", email: "", username: "", isLogin: false, }); } // 示例:登录接口调用 async LogIn(credentials) { try { const response = await axios.post("/auth/local", credentials); const { jwt, user } = response.data; this.setAuthState({ admin: user.admin, jwt, email: user.email, username: user.username, isLogin: true, }); } catch (error) { console.error("登录失败:", error); } } } export default AuthService;
3. Home.js(修改状态引用)
import Head from "next/head"; import Image from "next/image"; import styles from "../styles/Home.module.css"; import { useEffect, useState } from "react"; import Login from "./login"; import ClientPage from "./client"; import Dashboard from "./admin"; import fetchUrl from "./fetch"; import { useAuth } from "./Auth/AuthProvider"; export default function Home() { const [title, setTitle] = useState("Login duls Kang"); const [version, setVersion] = useState("1.0.0"); const { authState, authService, isLogin } = useAuth(); useEffect(() => { console.log("认证状态变化:", authState); }, [authState]); return ( <div> <Head> <title>{title}</title> <meta name="description" content="Generated by create next app" /> <link rel="icon" href="/favicon.ico" /> </Head> <div className="relative w-full pt-[5%] pb-[2%] h-screen min-h-fit flex flex-col items-center justify-start"> <div className="absolute w-[30%] md:w-[10%] min-h-fit text-[3.7vw] md:text-[1.4vw] left-[2%] top-[2%] flex flex-col justify-start items-start"> <div className="aspect-[20/5] w-[100%] left-[2%] top-[2%]"> Testing: {version} </div> <div className="aspect-[20/5] w-[100%] left-[2%] top-[6%]"> Auth? : {authState.admin ? "Admin" : "Client"} </div> <div className="aspect-[20/5] rounded-[5%/15%] flex justify-center items-center w-[100%] left-[2%] top-[10%] bg-[purple] text-[white]"> <button onClick={() => authService.logOut()} className="w-full h-full" > LogOut </button> </div> </div> <div className="w-[85%] md:w-[40%] mt-[25%] md:mt-[0%] min-h-fit h-screen bg-[grey] rounded-[2%/2%] flex flex-col items-center justify-start overflow-y-scroll"> {!isLogin && <Login></Login>} {isLogin && authState.admin && <Dashboard auth={authState} />} {isLogin && !authState.admin && <ClientPage />} </div> <div className="aspect-[200/50] w-[40%] bg-[white]"></div> </div> </div> ); }
4. Login.js(修改调用方式)
import { useState } from "react"; import { useAuth } from "./Auth/AuthProvider"; const Login = () => { const [credential, setCredential] = useState({ username: "", password: "" }); const { authService, isLogin } = useAuth(); return ( <> <div className="flex flex-col text-[3.75vw] md:text-[1.25vw] justify-start gap-[6%] items-center w-[100%] h-[70%]"> <div className="aspect-[200/35] w-[100%] bg-[lightgrey] text-center text-[6vw] md:text-[2vw] flex justify-center items-center rounded-[2%/10%]"> Login duls Bray </div> <div className="relative aspect-[200/30] md:aspect-[200/20] w-[70%] bg-[green] rounded-[2%/12%]"> <input className="absolute w-full h-full p-[5%] rounded-[2%/12%]" placeholder="username" onChange={(e) => { setCredential({ ...credential, username: e.target.value, }); }} /> </div> <div className="relative aspect-[200/30] md:aspect-[200/20] w-[70%] bg-[green] rounded-[2%/12%]"> <input className="absolute w-full h-full p-[5%] rounded-[2%/12%]" type="password" placeholder="password" onChange={(e) => { setCredential({ ...credential, password: e.target.value, }); }} /> </div> <div className="relative aspect-[200/30] md:aspect-[200/20] w-[70%] bg-[green] rounded-[2%/12%]"> <button onClick={async () => { const { username, password } = credential; setCredential({ username: "", password: "", }); await authService.LogIn({ identifier: username, password: password, }); }} className="absolute w-full h-full rounded-[2%/12%] hover:bg-[lightgreen] active:bg-[green]" > Login </button> </div> <div className="relative aspect-[200/30] md:aspect-[200/20] w-[70%] bg-[green] rounded-[2%/12%]"> <button onClick={() => { console.log("Logging in as guest..."); authService.LogInAsGuest(); }} className="absolute w-full h-full rounded-[2%/12%] hover:bg-[lightgreen] active:bg-[green]" > Log in as client </button> </div> </div> </> ); }; export default Login;
关键说明
- 把认证状态从
AuthService类内部移到AuthProvider的useState中,确保React能检测到状态变化并触发组件重新渲染。 AuthService不再存储状态,而是通过接收setAuthState函数来更新React管理的状态,实现状态同步。- 组件中直接使用
AuthProvider提供的authState和isLogin,无需调用currentUser()方法,状态更新会自动同步到所有消费组件。
内容的提问来源于stack exchange,提问作者HahahaHihihi
相关产品推荐
相关产品推荐

