You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.31 16:30:46