javascript - useNavigate() 未重定向到 '/protected'

标签 javascript reactjs react-router-dom

API 工作正常,它返回 token ,然后保存在我的本地内存中,但由于某种原因它不会重定向。如果我刷新窗口,我会直接进入 "/protected"页。

登录表单.js

import React, { useState } from 'react';
import { useNavigate } from 'react-router-dom';
import { useMutation } from 'react-query';
import api from './api';

function LoginForm() {
  const [email, setEmail] = useState('');
  const [password, setPassword] = useState('');
  const [error, setError] = useState('');

  const navigate = useNavigate();

  const handleLogin = async () => {
    const response = await api.post('/authenticate', { email, password });
    return response.data;
  };

  const { mutate, isLoading } = useMutation(handleLogin, {
    onSuccess: (data) => {
      localStorage.setItem('token', data.token);
      console.log(data.token);
      navigate('/protected');
    },
    onError: (error) => {
      setError(error.response.data);
    },
  });

  const handleSubmit = (e) => {
    mutate();
    e.preventDefault();
  };

  return (
    <form onSubmit={handleSubmit}>
      <label>
        Email:
        <input
          type="email"
          value={email}
          onChange={(e) => setEmail(e.target.value)}
        />
      </label>
      <label>
        Password:
        <input
          type="password"
          value={password}
          onChange={(e) => setPassword(e.target.value)}
        />
      </label>
      {error && <div>{error}</div>}
      <button type="submit" disabled={isLoading}>
        {isLoading ? 'Logging in...' : 'Login'}
      </button>
    </form>
  );
}

export default LoginForm;

这是我的App.js因为 protected 页面并不重要,因为它只有 <h1></h1> 。我的 API 也不适用,因为我毫无问题地获得了所需的 token 。唯一的问题是重定向。我确实强制窗口重新加载,但这是一种我打赌不安全的解决方法?

import {
  BrowserRouter as Router,
  Routes,
  Route,
  Navigate
} from 'react-router-dom';
import { useQuery } from 'react-query';
import LoginForm from './LoginForm';
import ProtectedResource from './ProtectedResource';
import api from './api';

function App() {
  const { isLoading, error, data: isLoggedIn } = useQuery('isLoggedIn', async () => {
    const token = localStorage.getItem('token');
    if (token) {
      try {
        await api.get('/resource', {
          headers: {
            Authorization: `Bearer ${token}`,
          },
        });
        return true;
      } catch (error) {
        if (error.response.status === 401) {
          localStorage.removeItem('token');
        }
        return false;
      }
    } else {
      return false;
    }
  });

  if (isLoading) return <p>Loading...</p>;
  if (error) return <p>An error occurred: {error.message}</p>;

  return (
    <Router>
      <Routes>
        <Route
          path="/"
          element={isLoggedIn ? <Navigate to="/protected" /> : <LoginForm />}
        />
        <Route
          path="/protected"
          element={isLoggedIn ? <ProtectedResource /> : <Navigate to="/" />}
        />
      </Routes>
    </Router>
  );
}

export default App;

最佳答案

代码使用 localStorage 作为事实来源,但更新 localStorage 不会触发 App React 组件使用新的身份验证值重新渲染,以便正确地进行渲染。必要时保护路由或重定向。 token 值存储在 localStorage 中,并且到 "/protected" 的导航操作生效,但由于 App 没有重新渲染,因此 useQuery钩子(Hook)没有重新运行来检查用户的 token 值。

重构代码以将状态添加到App,该状态在成功登录和退出时更新。这将触发App重新渲染并重新运行useQuery并检查存储的token值。

示例:

export default function App() {
  const [token, setToken] = React.useState(); // <-- state

  const { isLoading, error, data: isLoggedIn } = useQuery("isLoggedIn", async () => {
    const token = localStorage.getItem("token");
    if (token) {
      try {
        await api.get("/resource", {
          headers: {
            Authorization: `Bearer ${token}`
          }
        });
        return true;
      } catch (error) {
        if (error.response.status === 401) {
          localStorage.removeItem("token");
        }
        return false;
      }
    } else {
      return false;
    }
  });

  if (isLoading) return <p>Loading...</p>;
  if (error) return <p>An error occurred: {error.message}</p>;

  return (
    ...
      <Routes>
        <Route
          path="/"
          element={
            isLoggedIn ? (
              <Navigate to="/protected" />
            ) : (
              <LoginForm setToken={setToken} /> // <-- pass setter
            )
          }
        />
        <Route
          path="/protected"
          element={isLoggedIn ? <ProtectedResource /> : <Navigate to="/" />}
        />
      </Routes>
    ...
  );
}
function LoginForm({ setToken }) { // <-- consume setToken callback
  const [email, setEmail] = useState("");
  const [password, setPassword] = useState("");
  const [error, setError] = useState("");

  const navigate = useNavigate();

  const handleLogin = async () => {
    const response = await api.post("/authenticate", { email, password });
    return response.data;
  };

  const { mutate, isLoading } = useMutation(handleLogin, {
    onSuccess: (data) => {
      localStorage.setItem("token", data.token);
      setToken(data.token); // <-- update token state
      navigate("/protected");
    },
    onError: (error) => {
      setError(error.response.data);
    }
  });

  const handleSubmit = (e) => {
    mutate();
    e.preventDefault();
  };

  return (
    <form onSubmit={handleSubmit}>
      ...
    </form>
  );
}

演示

Edit usenavigate-not-redirecting-to-protected

关于javascript - useNavigate() 未重定向到 '/protected',我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/75982328/

相关文章:

reactjs - TypeScript React 功能组件 - 类型 'Element' : type, props 缺少以下属性,关键错误

javascript - 登录 React.js 后重定向

javascript - 关闭在 ZeroClipboard 中不起作用

javascript - 在网页上同时使用 JSP 和 Javascript 的设计模式

javascript - Meteor:为集合服务器端创建过滤器并将它们存储在本地集合中。好主意?

reactjs - useEffect 不是在组件渲染后运行吗?

javascript - 如何将 PHP 代码放入 JavaScript 中?

javascript - ESLint 错误/警告应该阻止在 React 中编译(create-react-app)

reactjs - 在 React Native 中使用多个上下文提供程序的更好方法

reactjs - React Router Dom 在网站加载时更改 URL