我在React应用程序中遇到了一个问题,我向登录端点发出API调用.API的响应正确返回,表明身份验证成功.然而,我面临着两个问题:

apiData和isAuth状态没有及时更新,导致导航到登录页面的发生在这些状态更新之前. 因此,应用程序导航到登录页面过快,而没有等待状态更新.

使用Api钩

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

const useApi = <T,>(url: string, method: string, payload?: T) => {
  const [apiData, setApiData] = useState<AxiosResponse>();
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState();

  useEffect(() => {
    const getData = async () => {
      try {
        const response = await axios.request({
          params: payload,
          method,
          url,
        });

        setApiData(response.data);
      } catch (e) {
        setError(e.message);
      } finally {
        setLoading(true);
      }
    };

    getData();
  }, []);

  return { apiData, loading, error };
};

export default useApi;

这就是形式

import { useState } from "react";
import { useNavigate } from "react-router-dom";

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

  const navigate = useNavigate();

  const submitForm = (e) => {
    e.preventDefault();
    navigate("/user/sign-in", { state: userInfo });
  };

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

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

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

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

      <button type="submit">submit</button>
    </form>
  );
};

privateRoute,我判断用户是否经过授权

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

export const PrivateRoute = () => {
  const location = useLocation();

  const { apiData, loading, error } = useApi(
    import.meta.env.VITE_SIGN_IN,
    "GET",
    location.state,
  );

  const [isAuth, setAuth] = useState<boolean>(false);

  useEffect(() => {
    if (apiData?.status === 200) {
      console.log(apiData, "api apiData");
      setAuth(true);
    }
  }, [apiData]);

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

推荐答案

初始isAuth状态false与稍后确认的未验证值false相匹配,因此在初始渲染周期中,将实现对登录页面的导航操作.useEffect挂钩在渲染周期结束时运行.

使用初始isAuth状态值,该值既不是true(代表已验证),也不是false(代表未验证),例如,您需要第三个值来指示"未知"状态.在身份验证判断运行并确认用户状态时,使用第三个值有条件地呈现为空或任何类型的加载指示符.

export const PrivateRoute = () => {
  const location = useLocation();

  const { apiData, loading, error } = useApi(
    import.meta.env.VITE_SIGN_IN,
    "GET",
    location.state,
  );

  const [isAuth, setAuth] = useState<boolean | undefined>(undefined);

  useEffect(() => {
    if (apiData) {
      console.log(apiData, "api apiData");
      setAuth(apiData.status === 200);
    }
  }, [apiData]);

  if (isAuth === undefined) {
    return null; // or loading spinner/indicator/etc
  }

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

isAuth值似乎是一个派生值,因此它实际上根本不需要是React状态,并且实际上通常被认为是这样做的React反模式.您可以直接从apiData计算出isAuth.

export const PrivateRoute = () => {
  const location = useLocation();

  const { apiData, loading, error } = useApi(
    import.meta.env.VITE_SIGN_IN,
    "GET",
    location.state,
  );

  // undefined | true | false
  const isAuth = apiData && apiData.status === 200;

  if (isAuth === undefined) {
    return null; // or loading spinner/indicator/etc
  }

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

Typescript相关问答推荐

如何将绑定到实例的类方法复制到类型脚本中的普通对象?

React Hook中基于动态收件箱定义和断言回报类型

Angular 过滤器形式基于某些条件的数组控制

Angular中的其他服务仅获取默认值

如何正确地对类型脚本泛型进行限制

为什么TypeScript假设文档对象始终可用?

如果字符串文字类型是泛型类型,则用反引号将该类型括起来会中断

我可以以这样一种方式键入对象,只允许它的键作为它的值吗?

错误TS2403:后续变量声明必须具有相同的类型.变量';CRYPTO';的类型必须是';CRYPATO';,但这里的类型是';CRYPATO';

已解决:如何使用值类型限制泛型键?

正交摄影机正在将渲染的场景切成两半

如何获取受类型脚本泛型约束的有效输入参数

TypeScrip-如何自动推断UNION变量的类型

接口中可选嵌套属性的类型判断

顶点堆叠的图表条形图未在正确的x轴上显示

推断集合访问器类型

在Vite中将SVG导入为ReactComponent-不明确的间接导出:ReactComponent

T的typeof键的Typescript定义

Karma Angular Unit测试如何创建未解析的promise并在单个测试中解决它,以判断当时的代码是否只在解决后运行

从泛型对象数组构造映射类型