React+Axios登录需刷新页面问题排查与解决求助
React + Axios 登录加载动画无法停止问题修复
我用React结合Axios实现登录功能时遇到问题:输入正确的邮箱和密码后,加载动画持续循环无法完成登录,仅刷新页面后重试才能成功进入应用。请问如何实现无需刷新页面即可正常登录?
相关代码
1. Login 组件
import axios from "axios"; import { useRef, useState } from "react"; import { storeToken } from "../utils/authServices"; import { useNavigate } from "react-router-dom"; import { useLoading } from "../context/hooks/useLoading"; import { LoginForm } from "../components"; export const Login = () => { const API_URL = "https://api.app"; // 出于安全隐藏真实API const { run } = useLoading(); const [error, setError] = useState(false); const [errorMessage, setErrorMessage] = useState(""); const navigate = useNavigate(); const correoRef = useRef(""); const passwordRef = useRef(""); const handleSubmit = async (e) => { e.preventDefault(); const { value: correo } = correoRef.current; const { value: password } = passwordRef.current; await axios .post(`${API_URL}/api/auth/login/`, { correo, password, }) .then((response) => { storeToken(response.data.token); run(); setTimeout(() => { navigate("/nueva-solicitud"); }, 1000); }) .catch((err) => { console.log(err.response.data); setError(true); setErrorMessage(err.response.data.msg); }); }; return ( <LoginForm correoRef={correoRef} passwordRef={passwordRef} handleSubmit={handleSubmit} error={error} errorMessage={errorMessage} /> ); };
2. AppContext 上下文
import { createContext, useReducer, useContext } from "react"; const initialState = { loading: false, alerts: [], }; const reducers = (state, action) => { switch (action.type) { case "LOADING_RUN": return { ...state, loading: true, }; case "LOADING_STOP": return { ...state, loading: false, }; default: return { ...state }; } }; const AppContext = createContext(); const AppContextProvider = (props) => { const [state, dispatch] = useReducer(reducers, initialState); return <AppContext.Provider value={{ state, dispatch }} {...props} />; }; const useAppContext = () => useContext(AppContext); export { AppContextProvider, useAppContext };
3. useLoading Hook
import { useMemo } from "react"; import { useAppContext } from "../AppContext"; export const useLoading = () => { const { dispatch } = useAppContext(); const loading = useMemo( () => ({ run: () => dispatch({ type: "LOADING_RUN" }), stop: () => dispatch({ type: "LOADING_STOP" }), }), [dispatch] ); return loading; };
4. authServices 工具函数
import jwt_decode from "jwt-decode"; export const storeToken = (token) => { localStorage.setItem("token", token); }; export const getToken = (decode = false) => { const token = localStorage.getItem("token"); if (decode) { const decoded = jwt_decode(token); return decoded; } return token; }; export const logout = () => { localStorage.removeItem("token"); };
问题分析与修复方案
核心问题
- 加载状态未终止:登录成功后仅调用
run()开启加载动画,但从未调用stop()关闭状态,导致loading一直为true,动画无限循环。 - 异步逻辑冗余:混用
await和.then()写法,且setTimeout延迟导航完全没必要,徒增不必要的等待。
修复后的 Login 组件代码
import axios from "axios"; import { useRef, useState } from "react"; import { storeToken } from "../utils/authServices"; import { useNavigate } from "react-router-dom"; import { useLoading } from "../context/hooks/useLoading"; import { LoginForm } from "../components"; export const Login = () => { const API_URL = "https://api.app"; const { run, stop } = useLoading(); const [error, setError] = useState(false); const [errorMessage, setErrorMessage] = useState(""); const navigate = useNavigate(); const correoRef = useRef(""); const passwordRef = useRef(""); const handleSubmit = async (e) => { e.preventDefault(); run(); // 先开启加载 setError(false); // 重置错误状态 try { const correo = correoRef.current.value; const password = passwordRef.current.value; const response = await axios.post(`${API_URL}/api/auth/login/`, { correo, password }); storeToken(response.data.token); // 导航前关闭加载 stop(); navigate("/nueva-solicitud"); } catch (err) { stop(); // 出错也要关闭加载 console.log(err.response.data); setError(true); setErrorMessage(err.response.data.msg); } }; return ( <LoginForm correoRef={correoRef} passwordRef={passwordRef} handleSubmit={handleSubmit} error={error} errorMessage={errorMessage} /> ); };
额外优化建议
- 用
useState代替useRef管理表单输入:useRef不会触发组件重渲染,若后续需要表单实时验证,useState更合适。 - 给Axios添加请求/响应拦截器:统一处理token携带和错误状态码,减少重复代码。
内容的提问来源于stack exchange,提问作者Kevstrosky
相关产品推荐
相关产品推荐

