React Context无法更新默认值:登录后状态始终为0求助
问题描述
我开发了一个登录表单,登录成功后尝试将React Context的值设置为1,但userStatus始终保持默认值0,即使硬编码设置也没有任何变化。
相关登录逻辑代码:
import { UserStatus, useUserStatus } from "../context/context"; const completeLoginAction = async (credentials: any) => { var loginSuccess = await authService.login(credentials); if (loginSuccess) { setUserStatus(1); localStorage.setItem("userStatus", JSON.stringify(userStatus)); console.log("USERSTATIS AFTER LOGIN IS" + userStatus); console.log( "local storage on login is " + localStorage.getItem("userStatus") ); return true; } else { return false; } };
context.tsx代码:
import { createContext, useContext } from "react"; export enum UserStatus { LoggedOff = 0, LoggedIn = 1, UserStatus, } export type UserStatusContextType = { userStatus: UserStatus; setUserStatus: (userStatus: UserStatus) => void; }; export const UserStatusContext = createContext<UserStatusContextType>({ userStatus: UserStatus.LoggedOff, setUserStatus: (userStatus) => console.warn("log on status unknown"), }); export const useUserStatus = () => useContext(UserStatusContext);
完整登录表单组件代码:
import React, { useContext, useEffect, useState } from "react"; import Button from "react-bootstrap/Button"; import Form from "react-bootstrap/Form"; import "bootstrap/dist/css/bootstrap.min.css"; import "./LoginPage.css"; import authService from "../../services/authService"; import { Navigate, useNavigate } from "react-router-dom"; import { UserStatus, useUserStatus } from "../context/context"; const LoginPage = () => { const [username, setUserName] = useState(""); const [password, setPassword] = useState(""); const [errMsg, setErrMsg] = useState(""); const [sucess, setSuccess] = useState(""); const { userStatus, setUserStatus } = useUserStatus(); const navigate = useNavigate(); const handleUsernameChange = (e: React.ChangeEvent<HTMLInputElement>) => { setUserName(e.target.value); }; const handlePasswordChange = (e: React.ChangeEvent<HTMLInputElement>) => { setPassword(e.target.value); }; const handleSave = async (e: React.MouseEvent<HTMLButtonElement>) => { e.preventDefault(); const credentials = { Username: username, Password: password, }; var completed = await completeLoginAction(credentials); // if (completed) { // console.log("logged in"); // navigate("/"); // } else { // showErrorMessage(); // } }; const completeLoginAction = async (credentials: any) => { var loginSuccess = await authService.login(credentials); if (loginSuccess) { setUserStatus(1); localStorage.setItem("userStatus", JSON.stringify(userStatus)); console.log("USERSTATIS AFTER LOGIN IS" + userStatus); console.log( "local storage on login is " + localStorage.getItem("userStatus") ); return true; } else { return false; } }; const showErrorMessage = () => { let elms = document.getElementsByClassName( "loginfailmessage" ) as HTMLCollectionOf<HTMLElement>; for (var i = 0; i < elms.length; i++) { elms[i].style.display = "block"; } }; return ( <div className="container"> <div className="LoginForm"> <h1 className="Title">Login</h1> <Form> <Form.Group className="mb-3" controlId="formBasicEmail"> <Form.Label>Username</Form.Label> <Form.Control type="text" placeholder="Enter username" onChange={handleUsernameChange} name="username" required /> </Form.Group> <Form.Group className="mb-3" controlId="formBasicPassword"> <Form.Label>Password</Form.Label> <Form.Control type="password" placeholder="Password" onChange={handlePasswordChange} name="password" required /> </Form.Group> <Button variant="primary" type="submit" onClick={handleSave}> Submit </Button> </Form> <h4 className="loginfailmessage" style={{ display: "none" }}> Failed to login </h4> </div> </div> ); }; export default LoginPage;
解决思路
1. 补全Context Provider配置
你只创建了Context,但没有在组件树的上层使用UserStatusContext.Provider包裹需要访问Context的组件。没有Provider的情况下,useUserStatus会一直使用Context的默认值,调用setUserStatus只会触发默认的警告函数,根本不会更新状态。
在根组件(比如App.tsx)中添加Provider:
import { useState } from 'react'; import { UserStatusContext, UserStatus } from './context/context'; import LoginPage from './LoginPage'; function App() { const [userStatus, setUserStatus] = useState<UserStatus>(UserStatus.LoggedOff); return ( <UserStatusContext.Provider value={{ userStatus, setUserStatus }}> {/* 此处放置路由或其他需要访问Context的组件 */} <LoginPage /> </UserStatusContext.Provider> ); } export default App;
2. 处理React状态更新的异步特性
setUserStatus是异步操作,调用后立刻读取userStatus拿到的还是更新前的旧值,所以你看到的console.log输出默认值是正常现象。
要将最新值同步到localStorage,有两种方案:
方案一:直接使用目标值存储
const completeLoginAction = async (credentials: any) => { var loginSuccess = await authService.login(credentials); if (loginSuccess) { const newStatus = UserStatus.LoggedIn; setUserStatus(newStatus); localStorage.setItem("userStatus", JSON.stringify(newStatus)); console.log("USERSTATIS AFTER LOGIN IS" + newStatus); return true; } else { return false; } };
方案二:用useEffect监听状态变化
在LoginPage组件中添加监听,每次userStatus更新时自动同步到localStorage:
useEffect(() => { localStorage.setItem("userStatus", JSON.stringify(userStatus)); }, [userStatus]);
3. 规范枚举值的使用
你的UserStatus枚举已经定义了LoggedIn = 1,直接使用枚举值比硬写数字更清晰,也避免出错:
setUserStatus(UserStatus.LoggedIn); // 替代setUserStatus(1)
另外,枚举中多余的UserStatus成员建议删除:
export enum UserStatus { LoggedOff = 0, LoggedIn = 1, }
4. 确保localStorage读取逻辑正确
后续读取localStorage时,记得解析JSON:
const savedStatus = localStorage.getItem("userStatus"); if (savedStatus) { const parsedStatus = parseInt(savedStatus, 10); // 可在根组件useState中用该值初始化Context状态 }
内容的提问来源于stack exchange,提问作者dros
相关产品推荐
相关产品推荐

