useNavigate 有时起作用,有时不起作用(在 API 调用之后)。

huangapple go评论81阅读模式
英文:

useNavigate sometimes works, sometimes doesn't (after API call)

问题

我理解你的问题,你遇到了路由重定向的问题。这个问题可能是因为你的路由保护逻辑有些问题。以下是一些可能的解决方案:

  1. 确保Token正确存储:在登录成功后,你应该确保Token正确存储在localStorage中。你已经在localStorage.setItem("token", responseData.token)这样做了,但要确保没有其他地方删除了这个Token。

  2. 检查ProtectedRoute逻辑:根据你提供的代码,问题可能出现在ProtectedRoute组件中。确保localStorage.getItem("token")返回正确的认证状态。你可以在App组件中的isAuthenticated变量上打印一些日志,以确保它的值是正确的。

  3. 确保Route正确匹配:确保你的路由配置是正确的。你已经将<ProtectedRoute {...defaultProtectedRouteProps} outlet={<Home />} />作为根路由的索引,这应该是正确的。但请确保没有其他地方有重复的路由匹配。

  4. 检查重定向触发时机:在handleLogin中,你设置了setTokenProvided(true),这应该触发路由重定向。确保这个部分按预期运行,也可以在这里添加一些日志来跟踪。

如果问题仍然存在,可能需要更多的代码和上下文来诊断问题。希望这些建议能帮助你找到问题所在。

英文:

I made a login page in TS React that sends an API request to authenticate the user.

The API call returns a token, after that the user should be redirected to &quot;/&quot; (if successful).

Returning the token works 100% every time, but the redirect sometimes fails.

It seems like the page is being rerendered instead of redirecting. What am I doing wrong?

import { useNavigate } from &quot;react-router-dom&quot;;

function Login() {
  const [username, setUsername] = useState&lt;string&gt;(&quot;&quot;);
  const [password, setPassword] = useState&lt;string&gt;(&quot;&quot;);
  const [tokenProvided, setTokenProvided] = React.useState&lt;boolean&gt;(false);

  const navigate = useNavigate();
  
  useEffect(() =&gt; {
    document.title = &quot;Login&quot;;
    if(tokenProvided){
      navigate(&#39;/&#39;)
    }
  }, [tokenProvided]);


  const handleLogin = async (event: React.FormEvent&lt;HTMLFormElement&gt;) =&gt; {
    event.preventDefault();
    try {
      const loginData: loginData = { username, password };
      const responseData = await getLoginToken(loginData);
      localStorage.setItem(&quot;token&quot;, responseData.token);
      const userAuthentificationToken = localStorage.getItem(&quot;token&quot;);
      axios.defaults.headers.common[&quot;Authorization&quot;] = `Bearer ${userAuthentificationToken}`;
      setTokenProvided(true);
    } catch (error) {
      createErrorToast(&quot;Invalid username or password.&quot;);
    }
  return (
    &lt;Page backgroundColorActive={false} navbarActive={false}&gt;
      &lt;LoginFormWrapper&gt;
        ...bla...

        &lt;LoginForm onSubmit={handleLogin}&gt;
          ...bla...
          &lt;LoginFormButton type=&quot;submit&quot;&gt;Login&lt;/LoginFormButton&gt;
        &lt;/LoginForm&gt;
      &lt;/LoginFormWrapper&gt;
    &lt;/Page&gt;
  );
}

export default Login;

The behaviors is as follows:

  • I enter the credentials on the page
  • I hit submit
  • I can see it working (takes a moment for the api call to return)
  • The site rerenders
  • I can see the token in local storage

Sometimes it will in fact redirect, but I don't know why or how or when.

Edit:
Here is my route protection:

App.tsx:

import React from &quot;react&quot;;
import { BrowserRouter, Routes, Route } from &quot;react-router-dom&quot;;
import ProtectedRoute, { ProtectedRouteProps } from &quot;./helper/restrictedRoute&quot;;

const isAuthenticated = localStorage.getItem(&quot;token&quot;) !== null;
const defaultProtectedRouteProps: Omit&lt;ProtectedRouteProps, &quot;outlet&quot;&gt; = {
  isAuthenticated: isAuthenticated,
  authenticationPath: &quot;/login&quot;,
};

function App() {
  return (
    &lt;BrowserRouter&gt;
      &lt;Routes&gt;
        {/* Unprotected routes: These routes can be accessed without being logged in */}
        &lt;Route path=&quot;/login&quot; element={&lt;Login /&gt;} /&gt;
        {/* Protected routes: These routes can only be accessed while being logged in */}
         &lt;Route index element={&lt;ProtectedRoute {...defaultProtectedRouteProps} outlet={&lt;Home /&gt;} /&gt;} /&gt;
      &lt;/Routes&gt;
    &lt;/BrowserRouter&gt;
  );
}
export default App;

RestrictedRoute.tsx:

import { Navigate } from &quot;react-router-dom&quot;;

export type ProtectedRouteProps = {
  isAuthenticated: boolean;
  authenticationPath: string;
  outlet: JSX.Element;
};

export default function ProtectedRoute({ isAuthenticated, authenticationPath, outlet }: ProtectedRouteProps) {
  if (isAuthenticated) {
    return outlet;
  } else {
    return &lt;Navigate to={{ pathname: authenticationPath }} /&gt;;
  }
}

The routing protection seems to be the problem, the else statement in RestrictedRoute.tsx gets executed, but I don't know how to fix that.

答案1

得分: 1

以下是代码的中文翻译:

  1. 当处理/挂载“App”文件/组件时,仅会检查一次localStorage。 “isAuthenticated”在应用程序之外计算一次。
const isAuthenticated = localStorage.getItem("token") !== null;
  1. 应将localStorage检查移到“ProtectedRoute”中,并在组件渲染时进行检查。 “ProtectedRoute”还应呈现“Outlet”组件,以便保护嵌套路由将其内容呈现为。
import { Navigate, Outlet } from "react-router-dom";

export type ProtectedRouteProps = {
  authenticationPath: string;
};

export default function ProtectedRoute({ authenticationPath }: ProtectedRouteProps) {
  const isAuthenticated = localStorage.getItem("token") !== null;

  return isAuthenticated
    ? <Outlet />
    : <Navigate to={authenticationPath} replace />;
}
  1. 路由如下编写:
import React from "react";
import { BrowserRouter, Routes, Route } from "react-router-dom";
import ProtectedRoute, { ProtectedRouteProps } from "./helper/restrictedRoute";

const defaultProtectedRouteProps: ProtectedRouteProps = {
  authenticationPath: "/login",
};

function App() {
  return (
    <BrowserRouter>
      <Routes>
        {/* 未受保护的路由:未登录用户可以访问这些路由 */}
        <Route path="/login" element={<Login />} />

        {/* 受保护的路由:只有登录用户才能访问这些路由 */}
        <Route element={<ProtectedRoute {...defaultProtectedRouteProps} />}>
          <Route path="/" element={<Home />} />
        </Route>
      </Routes>
    </BrowserRouter>
  );
}
export default App;
英文:

The localStorage is checked only once when the App file/component is processed/mounted. isAuthenticated is computed once outside anything in the app.

const isAuthenticated = localStorage.getItem(&quot;token&quot;) !== null;

The localStorage check should be moved into the ProtectedRoute and checked when the component renders. ProtectedRoute should also render the Outlet component for protected nested routes to render their content into.

import { Navigate, Outlet } from &quot;react-router-dom&quot;;

export type ProtectedRouteProps = {
  authenticationPath: string;
};

export default function ProtectedRoute({ authenticationPath }: ProtectedRouteProps) {
  const isAuthenticated = localStorage.getItem(&quot;token&quot;) !== null;

  return isAuthenticated
    ? &lt;Outlet /&gt;
    : &lt;Navigate to={authenticationPath} replace /&gt;;
}

The routes written as follows:

import React from &quot;react&quot;;
import { BrowserRouter, Routes, Route } from &quot;react-router-dom&quot;;
import ProtectedRoute, { ProtectedRouteProps } from &quot;./helper/restrictedRoute&quot;;

const defaultProtectedRouteProp: ProtectedRouteProps = {
  authenticationPath: &quot;/login&quot;,
};

function App() {
  return (
    &lt;BrowserRouter&gt;
      &lt;Routes&gt;
        {/* Unprotected routes: These routes can be accessed without being logged in */}
        &lt;Route path=&quot;/login&quot; element={&lt;Login /&gt;} /&gt;

        {/* Protected routes: These routes can only be accessed while being logged in */}
         &lt;Route element={&lt;ProtectedRoute {...defaultProtectedRouteProps} /&gt;}&gt;
           &lt;Route path=&quot;/&quot; element={&lt;Home /&gt;} /&gt;
        &lt;/Route&gt;
      &lt;/Routes&gt;
    &lt;/BrowserRouter&gt;
  );
}
export default App;

huangapple
  • 本文由 发表于 2023年4月13日 22:57:15
  • 转载请务必保留本文链接:https://go.coder-hub.com/76006941.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定