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

React应用useState更新前组件渲染致登录跳转异常问题

问题解决:React登录API状态更新不及时与跳转过早问题

核心问题分析

  • useApi Hook 缺陷:依赖项为空数组导致参数变化时不重新请求;loading状态逻辑颠倒,无法标识加载过程;响应数据类型存储错误。
  • 登录流程逻辑错误:登录表单未验证就跳转路由,PrivateRoute因初始isAuth=false直接触发回跳。
  • PrivateRoute 未处理加载状态:未等待API请求完成就执行跳转,忽略请求中的过渡状态。

修复后的代码实现

1. 修正 useApi Hook

import axios from "axios";
import { useEffect, useState } from "react";

// T: 请求参数类型,R: 响应数据类型
const useApi = <T, R = unknown>(url: string, method: string, payload?: T) => {
  const [apiData, setApiData] = useState<R>();
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState<string>();

  useEffect(() => {
    const fetchData = async () => {
      setLoading(true);
      try {
        const config = {
          method,
          url,
          // 根据请求方法区分参数传递方式
          ...(method.toUpperCase() === "GET" ? { params: payload } : { data: payload })
        };
        const response = await axios(config);
        setApiData(response.data);
      } catch (e: any) {
        setError(e?.response?.data?.message || e.message || "请求失败");
      } finally {
        setLoading(false);
      }
    };

    fetchData();
  }, [url, method, payload]); // 添加依赖项,参数变化时重新请求

  return { apiData, loading, error };
};

export default useApi;

2. 重构登录表单组件(SignIn)

import { useState, useEffect } from "react";
import { useNavigate } from "react-router-dom";
import useApi from "../../api/useApi";

export const SignIn = () => {
  const [userInfo, setUserInfo] = useState({
    user: "",
    password: "",
  });
  const navigate = useNavigate();

  // 提交时调用登录API
  const { apiData, loading, error } = useApi<
    typeof userInfo,
    { status: number; token?: string }
  >(import.meta.env.VITE_SIGN_IN, "POST", userInfo);

  // 监听API响应,验证成功后跳转
  useEffect(() => {
    if (apiData?.status === 200) {
      // 持久化存储token
      localStorage.setItem("authToken", apiData.token);
      // 跳转到受保护的仪表盘页面
      navigate("/user/dashboard", { replace: true });
    }
  }, [apiData, navigate]);

  const submitForm = (e: React.FormEvent) => {
    e.preventDefault();
  };

  const updateUser = (newVal: string) => {
    setUserInfo((prev) => ({ ...prev, user: newVal }));
  };

  const updatePassword = (newVal: string) => {
    setUserInfo((prev) => ({ ...prev, password: newVal }));
  };

  return (
    <form onSubmit={submitForm}>
      <label>username</label>
      <input
        type="text"
        value={userInfo.user}
        onChange={(e) => updateUser(e.target.value)}
        disabled={loading}
      />

      <label>password</label>
      <input
        type="password"
        value={userInfo.password}
        onChange={(e) => updatePassword(e.target.value)}
        disabled={loading}
      />

      {error && <p style={{ color: "red" }}>{error}</p>}
      
      <button type="submit" disabled={loading}>
        {loading ? "登录中..." : "提交"}
      </button>
    </form>
  );
};

3. 修正 PrivateRoute 组件

import { useState, useEffect } from "react";
import { Navigate, useLocation } from "react-router-dom";
import UserDashboard from "./UserDashboard";
import useApi from "../../api/useApi";

export const PrivateRoute = () => {
  const location = useLocation();
  const [isAuth, setIsAuth] = useState<boolean | null>(null); // 初始为null表示未完成验证
  const token = localStorage.getItem("authToken");

  // 可选:验证token有效性
  const { apiData, loading } = useApi<
    { token: string },
    { valid: boolean }
  >(import.meta.env.VITE_VALIDATE_TOKEN, "POST", { token });

  useEffect(() => {
    if (!loading) {
      // 结合本地存储和API验证结果判断权限
      if (token && (apiData?.valid ?? true)) {
        setIsAuth(true);
      } else {
        setIsAuth(false);
      }
    }
  }, [token, apiData, loading]);

  // 加载中显示过渡状态
  if (isAuth === null || loading) {
    return <div>加载中...</div>;
  }

  return isAuth ? (
    <UserDashboard />
  ) : (
    <Navigate to="/sign-in" state={{ from: location.pathname }} replace />
  );
};

关键修复点说明

  • useApi 优化:修正loading状态逻辑,添加正确依赖项,适配不同请求方法的参数传递。
  • 登录流程调整:在表单内完成API验证,成功后再跳转,禁用加载中元素防止重复提交。
  • PrivateRoute 改进:初始状态设为null避免误跳转,处理加载状态,结合本地存储实现持久化认证。

内容的提问来源于stack exchange,提问作者Ali Shahen

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.25 21:34:53