`useNavigate()`没有重定向到’/protected’。

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

useNavigate() not redirecting to '/protected'

问题

以下是您要翻译的代码部分:

LoginForm.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:

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;
英文:

The API is working fine, it returns the token that then is saved in my local memory but it doesn't redirect for some reason. If I refresh my window it takes me straight to the &quot;/protected&quot; page.

LoginForm.js

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

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

  const navigate = useNavigate();

  const handleLogin = async () =&gt; {
    const response = await api.post(&#39;/authenticate&#39;, { email, password });
    return response.data;
  };

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

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

  return (
    &lt;form onSubmit={handleSubmit}&gt;
      &lt;label&gt;
        Email:
        &lt;input
          type=&quot;email&quot;
          value={email}
          onChange={(e) =&gt; setEmail(e.target.value)}
        /&gt;
      &lt;/label&gt;
      &lt;label&gt;
        Password:
        &lt;input
          type=&quot;password&quot;
          value={password}
          onChange={(e) =&gt; setPassword(e.target.value)}
        /&gt;
      &lt;/label&gt;
      {error &amp;&amp; &lt;div&gt;{error}&lt;/div&gt;}
      &lt;button type=&quot;submit&quot; disabled={isLoading}&gt;
        {isLoading ? &#39;Logging in...&#39; : &#39;Login&#39;}
      &lt;/button&gt;
    &lt;/form&gt;
  );
}

export default LoginForm;

Here is my App.js as the protected page does not matter as it only has an &lt;h1&gt;&lt;/h1&gt;. My API does not apply either as I get the required token without an issue. The only issue is redirecting. I did force window reload but that is a work-around that I bet is not safe?

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

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

  if (isLoading) return &lt;p&gt;Loading...&lt;/p&gt;;
  if (error) return &lt;p&gt;An error occurred: {error.message}&lt;/p&gt;;

  return (
    &lt;Router&gt;
      &lt;Routes&gt;
        &lt;Route
          path=&quot;/&quot;
          element={isLoggedIn ? &lt;Navigate to=&quot;/protected&quot; /&gt; : &lt;LoginForm /&gt;}
        /&gt;
        &lt;Route
          path=&quot;/protected&quot;
          element={isLoggedIn ? &lt;ProtectedResource /&gt; : &lt;Navigate to=&quot;/&quot; /&gt;}
        /&gt;
      &lt;/Routes&gt;
    &lt;/Router&gt;
  );
}

export default App;

答案1

得分: 2

以下是您要翻译的内容:

代码中使用localStorage作为数据源,但更新localStorage不会触发App React组件重新渲染,以显示新的身份验证值,以便正确保护路由或在必要时重定向。令牌值存储在localStorage中,导航到"/protected"已生效,但由于App未重新渲染,useQuery钩子未重新运行以检查用户的令牌值。

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

示例:

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

  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} /> // <-- 传递设置器
            )
          }
        />
        <Route
          path="/protected"
          element={isLoggedIn ? <ProtectedResource /> : <Navigate to="/" />}
        />
      </Routes>
    ...
  );
}
function LoginForm({ setToken }) { // <-- 使用setToken回调
  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); // <-- 更新令牌状态
      navigate("/protected");
    },
    onError: (error) => {
      setError(error.response.data);
    }
  });

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

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

演示链接:

`useNavigate()`没有重定向到’/protected’。

英文:

The code is using localStorage as the source of truth, but updating localStorage doesn't trigger the App React component to rerender with the new authentication value in order to correctly protect routes or redirect when necessary. The token value is stored in localStorage and the navigation action to &quot;/protected&quot; is effected, but because App didn't rerender, the useQuery hook didn't rerun to check the user's token value.

Refactor the code to add state to App that is updated upon successful log in & out. This will trigger App to rerender and rerun useQuery and check the stored token value.

Example:

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

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

  if (isLoading) return &lt;p&gt;Loading...&lt;/p&gt;;
  if (error) return &lt;p&gt;An error occurred: {error.message}&lt;/p&gt;;

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

  const navigate = useNavigate();

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

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

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

  return (
    &lt;form onSubmit={handleSubmit}&gt;
      ...
    &lt;/form&gt;
  );
}

Demo

`useNavigate()`没有重定向到’/protected’。

huangapple
  • 本文由 发表于 2023年4月11日 12:07:27
  • 转载请务必保留本文链接:https://go.coder-hub.com/75982328.html
匿名

发表评论

匿名网友

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

确定