React AuthContext无法将数据存入LocalStorage问题求助
MERN项目React Context认证LocalStorage写入失败问题
我在跟着Freecodecamp教程做MERN项目,用React Context实现认证功能,目前碰到以下问题:
- 登录请求能正常发送到后端,但数据无法存入LocalStorage
- 之前用其他方式实现认证时,首次登录userId为undefined,刷新页面后才正常,且无法将userId传给后端;更换React Context方案后,LocalStorage写入问题仍未解决
相关代码
App.js
import React, { useState } from "react"; import { Navigate, Router, Routes, Route } from "react-router-dom"; // Pages import { Home, Dashboard, MembersPage, ProfilePage, CreatePost, OnePost, } from "./Pages/index"; export const AuthContext = React.createContext(); const initialState = { token: null, userId: null, isAuthenticated: false, }; const reducer = (state, action) => { switch (action.type) { case "LOGIN": localStorage.setItem("token", JSON.stringify(action.payload.token)); localStorage.setItem("userId", JSON.stringify(action.payload.userId)); localStorage.setItem( "firstName", JSON.stringify(action.payload.firstName) ); localStorage.setItem("lastName", JSON.stringify(action.payload.lastName)); localStorage.setItem("email", JSON.stringify(action.payload.email)); localStorage.setItem( "isAuthenticated", JSON.stringify(action.payload.isAuthenticated) ); return { ...state, token: action.payload.token, userId: action.payload.user, isAuthenticated: true, }; case "LOGOUT": localStorage.clear(); return { ...state, token: null, userId: null, isAuthenticated: false, }; default: return state; } }; function App() { const [state, dispatch] = React.useReducer(reducer, initialState); return ( <AuthContext.Provider value={{ state, dispatch, }} > <Routes> <Route path="/" exact element={<Home />} /> <Route path="/dashboard" exact element={<Dashboard />} /> <Route path="/profile" exact element={<ProfilePage />} /> <Route path="/members" exact element={<MembersPage />} /> <Route path="/posts" exact element={<CreatePost />} /> <Route path="/posts/:id" exact element={<OnePost />} /> <Route path="*" element={<Navigate to="/" replace />} /> </Routes> </AuthContext.Provider> ); } export default App;
Login.jsx
const Login = () => { const navigate = useNavigate(); const { dispatch } = React.useContext(AuthContext); const initialState = { email: "", password: "", isSubmitting: false, errorMessage: null, }; const [data, setData] = React.useState(initialState); const handleInputChange = (event) => { setData({ ...data, [event.target.name]: event.target.value, }); }; const handleLogin = (event) => { event.preventDefault(); setData({ ...data, isSubmitting: true, errorMessage: null, }); axios({ method: "post", headers: { "Access-Control-Allow-Origin": "*" }, url: "http://localhost:8080/home/login", data: { email: data.email, password: data.password, }, }) .then((res) => { navigate("/dashboard"); if (res.ok) { return res.json(); } throw res; }) .then((resJson) => { dispatch({ type: "LOGIN", payload: resJson, }); }) .catch((error) => { console.log(error); setData({ ...data, isSubmitting: false, errorMessage: error.message || error.statusText, }); }); }; return ( <Box component="form" onSubmit={handleLogin} noValidate > <label htmlFor="email"> Email Address <input type="text" value={data.email} onChange={handleInputChange} name="email" id="email" /> </label> <label htmlFor="password"> Password <input type="password" value={data.password} onChange={handleInputChange} name="password" id="password" /> </label> {data.errorMessage && ( <span className="form-error">{data.errorMessage}</span> )} <button disabled={data.isSubmitting}> {data.isSubmitting ? "Loading..." : "Login"} </button> <Grid container></Grid> </Box> ); }; export default Login;
问题修复方案
1. 调整导航执行时机
当前Login组件在请求返回后立即跳转,导致后续dispatch可能因组件卸载而无法执行。将navigate移到dispatch之后:
.then((resJson) => { dispatch({ type: "LOGIN", payload: resJson, }); navigate("/dashboard"); // 移至dispatch完成后执行 })
2. 修正Reducer字段不匹配问题
LOGIN分支中,存入LocalStorage的是action.payload.userId,但返回状态时写的是action.payload.user,字段不统一,修改为:
return { ...state, token: action.payload.token, userId: action.payload.userId, // 改为与LocalStorage存入的字段一致 isAuthenticated: true, };
3. 初始化时从LocalStorage恢复状态
页面刷新后Context状态会重置为初始值,需在App组件初始化时读取LocalStorage数据:
function App() { // 从LocalStorage加载已有认证数据 const initialStateFromStorage = { token: localStorage.getItem("token") ? JSON.parse(localStorage.getItem("token")) : null, userId: localStorage.getItem("userId") ? JSON.parse(localStorage.getItem("userId")) : null, firstName: localStorage.getItem("firstName") ? JSON.parse(localStorage.getItem("firstName")) : null, lastName: localStorage.getItem("lastName") ? JSON.parse(localStorage.getItem("lastName")) : null, email: localStorage.getItem("email") ? JSON.parse(localStorage.getItem("email")) : null, isAuthenticated: localStorage.getItem("isAuthenticated") ? JSON.parse(localStorage.getItem("isAuthenticated")) : false, }; const [state, dispatch] = React.useReducer(reducer, initialStateFromStorage); // 其余代码保持不变 }
4. 移除无效的前端CORS头
前端请求中设置Access-Control-Allow-Origin是无效的,该Header由后端配置,删除该字段:
axios({ method: "post", // 移除headers: { "Access-Control-Allow-Origin": "*" } url: "http://localhost:8080/home/login", data: { email: data.email, password: data.password, }, })
5. 验证后端返回字段
确认后端/home/login接口返回的JSON包含token、userId、firstName、lastName、email字段,无拼写错误(比如后端返回user_id而非userId时需对应调整)。
内容的提问来源于stack exchange,提问作者Fenrir
相关产品推荐
相关产品推荐

