React+Redux登录成功后跳转仪表盘闪回异常求助
React + Redux 登录功能异常排查与修复
问题现象
- 登录页面初始渲染时,Redux中
user值为null、isSuccess值为false; - 点击「Sign In」按钮后,仪表盘仅显示1秒就跳回登录页,此时
user和isSuccess重置为初始值; - 连续点击登录按钮5-10次后,偶尔能成功停留在仪表盘。
相关代码
登录页面代码
const Login = () => { const [email, setEmail] = useState(""); const [password, setPassword] = useState(""); const dispatch = useDispatch(); const navigate = useNavigate(); const { user, isError, isSuccess, isLoading, message } = useSelector( (state) => state.auth ); useEffect(() => { if (user || isSuccess) { navigate("/dashboard"); } dispatch(reset()); }, [user, isSuccess, dispatch, navigate]); const Auth = (e) => { e.preventDefault(); dispatch(LoginUser({ email, password })); }; return ( <div className="wrapper"> <main className="authentication-content"> <div className="container-fluid"> <div className="authentication-card"> <div className="card shadow rounded-0 overflow-hidden"> <div className="row g-0"> <div className="col-lg-6 bg-login d-flex align-items-center justify-content-center"> <img src="assets/images/error/login-img.jpg" className="img-fluid" alt=""/> </div> <div className="col-lg-6"> <div className="card-body p-4 p-sm-5"> <h5 className="card-title">Sign In</h5> <p className="card-text mb-5">See your growth and get consulting support!</p> <form onSubmit={Auth} className="form-body"> {isError && <p className="has-text-centered">{message}</p>} <div className="row g-3"> <div className="col-12"> <label htmlFor="inputEmailAddress" className="form-label">Email Address</label> <div className="ms-auto position-relative"> <div className="position-absolute top-50 translate-middle-y search-icon px-3"><i className="bi bi-envelope-fill"></i></div> <input type="email" value={email} onChange={(e) => setEmail(e.target.value)} className="form-control radius-30 ps-5" id="inputEmailAddress" placeholder="Email Address"/> </div> </div> <div className="col-12"> <label htmlFor="inputChoosePassword" className="form-label">Enter Password</label> <div className="ms-auto position-relative"> <div className="position-absolute top-50 translate-middle-y search-icon px-3"><i className="bi bi-lock-fill"></i></div> <input type="password" value={password} onChange={(e) => setPassword(e.target.value)} className="form-control radius-30 ps-5" id="inputChoosePassword" placeholder="Enter Password"/> </div> </div> <div className="col-6"> <div className="form-check form-switch"> <input className="form-check-input" type="checkbox" id="flexSwitchCheckChecked" /> <label className="form-check-label" htmlFor="flexSwitchCheckChecked">Remember Me</label> </div> </div> <div className="col-6 text-end"> <a href="authentication-forgot-password.html">Forgot Password ?</a> </div> <div className="col-12"> <div className="d-grid"> <button type="submit" className="btn btn-primary radius-30">{isLoading ? "Loading..." : "Sign In"}</button> </div> </div> </div> </form> </div> </div> </div> </div> </div> </div> </main> </div> ) } export default Login;
AuthSlice代码
import { createSlice, createAsyncThunk } from "@reduxjs/toolkit"; import axios from "axios"; const initialState = { user: null, isError: false, isSuccess: false, isLoading: false, message: "" } export const LoginUser = createAsyncThunk("user/LoginUser", async(user, thunkAPI) => { try { const response = await axios.post('http://localhost:5001/login', { email: user.email, password: user.password }); return response.data; } catch (error) { if(error.response){ const message = error.response.data.msg; return thunkAPI.rejectWithValue(message); } } }); export const getMe = createAsyncThunk("user/getMe", async(_, thunkAPI) => { try { const response = await axios.get('http://localhost:5001/me'); return response.data; } catch (error) { if(error.response){ const message = error.response.data.msg; return thunkAPI.rejectWithValue(message); } } }); export const LogOut = createAsyncThunk("user/LogOut", async() => { await axios.delete('http://localhost:5001/logout'); }); export const authSlice = createSlice({ name: "auth", initialState, reducers:{ reset: (state) => initialState }, extraReducers:(builder) =>{ builder.addCase(LoginUser.pending, (state) =>{ state.isLoading = true; }); builder.addCase(LoginUser.fulfilled, (state, action) =>{ state.isLoading = false; state.isSuccess = true; state.user = action.payload; }); builder.addCase(LoginUser.rejected, (state, action) =>{ state.isLoading = false; state.isError = true; state.message = action.payload; }) // Get User Login builder.addCase(getMe.pending, (state) =>{ state.isLoading = true; }); builder.addCase(getMe.fulfilled, (state, action) =>{ state.isLoading = false; state.isSuccess = true; state.user = action.payload; }); builder.addCase(getMe.rejected, (state, action) =>{ state.isLoading = false; state.isError = true; state.message = action.payload; }) } }); export const {reset} = authSlice.actions; export default authSlice.reducer;
仪表盘页面代码
const Dashboard = () => { const dispatch = useDispatch(); const navigate = useNavigate(); const { isError } = useSelector((state) => state.auth); useEffect(() => { dispatch(getMe()); }, [dispatch]); useEffect(() => { if (isError) { navigate("/"); } }, [isError, navigate]); return ( <Layout> <Welcome /> </Layout> ); }; export default Dashboard;
问题根源与修复方案
1. 核心问题:登录页reset调用时机错误
登录页的useEffect在user/isSuccess变化时,先导航到仪表盘,随即调用dispatch(reset())直接清空Redux状态。导致仪表盘加载时getMe请求拿不到有效状态,触发isError后跳回登录页。
修复代码(登录页):
useEffect(() => { // 登录成功时导航,不执行reset if (user || isSuccess) { navigate("/dashboard"); return; } // 仅登录失败时重置错误状态 if (isError) { setTimeout(() => dispatch(reset()), 3000); } }, [user, isSuccess, isError, dispatch, navigate]);
2. 辅助问题:axios未携带登录凭证
如果后端用Session认证,axios默认不会自动携带Cookie,导致/me请求返回未授权,触发isError。
修复代码(AuthSlice顶部):
// 配置axios自动携带跨域Cookie axios.defaults.withCredentials = true;
同时确保后端CORS配置开启credentials: true。
3. 仪表盘逻辑优化
避免重复调用getMe,仅在无用户信息时验证身份:
const Dashboard = () => { const dispatch = useDispatch(); const navigate = useNavigate(); const { isError, user, isSuccess } = useSelector((state) => state.auth); useEffect(() => { if (!user && !isSuccess) { dispatch(getMe()); } }, [dispatch, user, isSuccess]); useEffect(() => { if (isError) { navigate("/"); dispatch(reset()); } }, [isError, navigate, dispatch]); return ( <Layout> <Welcome /> </Layout> ); };
4. AuthSlice状态管理优化
在登录失败、登出成功时自动重置状态,避免手动调用失误:
extraReducers:(builder) =>{ // ... 原有LoginUser逻辑 builder.addCase(LoginUser.rejected, (state, action) =>{ state.isLoading = false; state.isError = true; state.message = action.payload; // 3秒后自动清除错误状态 setTimeout(() => { state.isError = false; state.message = ""; }, 3000); }); // ... 原有getMe逻辑 // 登出成功后重置状态 builder.addCase(LogOut.fulfilled, (state) =>{ state.user = null; state.isSuccess = false; }); }
内容的提问来源于stack exchange,提问作者CodeQ Technology
相关产品推荐
相关产品推荐

