我已经为特定的页面打了PrivateRoute分.除PrivateRoute页外,所有页都是公共的(Anyone can view them).如果用户是未经授权的,则路由始终导航到在logUser函数中声明的页面.

我正在使用Python Django来构建API.

App.js

import "./App.css";
import { Fragment } from "react";
import { BrowserRouter, Routes, Route } from "react-router-dom";
import Home from "./pages/Home";
import Login from "./pages/Login";
import Register from "./pages/Register";
import Post from "./pages/Post";
import Details from "./pages/Details";
import HeaderNav from "./components/Navbar";
import Footerbar from "./components/Footer";
import PrivateRoute from "./utils/PrivateRoute";
import { AuthProvider } from "./context/AuthContext";

function App() {
  return (
    <Fragment>
      <BrowserRouter>
        <AuthProvider>
          <HeaderNav />
          <div className="bg-gray-100 w-full h-full">
            <Routes>
              <Route element={<PrivateRoute />} path="/post">
                <Route element={<Post />} path="/post" />
              </Route>
              <Route element={<Home />} path="/" />
              <Route element={<Post />} path="/post" />
              <Route element={<Details />} path="/post/:id" />
              <Route element={<Register />} path="/register" />
              <Route element={<Login />} path="/login" />
            </Routes>
          </div>
          <Footerbar />
        </AuthProvider>
      </BrowserRouter>
    </Fragment>
  );
}

export default App;

PrivateRoute.js

import { Navigate, Outlet } from "react-router-dom";
import { useContext } from "react";
import AuthContext from "../context/AuthContext";

const PrivateRoute = () => {
  const { username } = useContext(AuthContext);
  return username ? <Outlet /> : <Navigate to="/login" />;
};

export default PrivateRoute;

AuthContext.js

import { createContext, useEffect, useState } from "react";
import jwt_decode from "jwt-decode";
import { useNavigate } from "react-router-dom";

const AuthContext = createContext();

export default AuthContext;

export const AuthProvider = ({ children }) => {
  let [authTokens, setAuthTokens] = useState(() =>
    localStorage.getItem("authTokens")
      ? JSON.parse(localStorage.getItem("authTokens"))
      : null
  );
  let [username, setUsername] = useState(() =>
    localStorage.getItem("authTokens")
      ? jwt_decode(localStorage.getItem("authTokens"))
      : null
  );
  let [loading, setLoading] = useState(true);

  const navigate = useNavigate();

  let loginUser = async (e) => {
    e.preventDefault();
    const response = await fetch("http://127.0.0.1:8000/api/token/", {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
      },
      body: JSON.stringify({
        username: e.target.username.value,
        password: e.target.password.value,
      }),
    });
    let data = await response.json();

    if (response.status === 200) {
      setAuthTokens(data);
      setUsername(jwt_decode(data.access));
      localStorage.setItem("authTokens", JSON.stringify(data));
      navigate("/");
    } else {
      alert("Incorrect username or password!");
    }
  };

  const logoutUser = () => {
    setAuthTokens(null);
    setUsername(null);
    localStorage.removeItem("authTokens");
    navigate("/"); // Always navigating this page when the user is unauthorized
  };

  const updateToken = async () => {
    const response = await fetch(
      "http://127.0.0.1:8000/api/token/refresh/",
      {
        method: "POST",
        headers: {
          "Content-Type": "application/json",
        },
        body: JSON.stringify({ refresh: authTokens?.refresh }),
      }
    );
    let data = await response.json();
    if (response.status === 200) {
      setAuthTokens(data);
      setUsername(jwt_decode(data.access));
      localStorage.setItem("authTokens", JSON.stringify(data));
    } else {
      logoutUser();
    }

    if (loading) {
      setLoading(false);
    }
  };

  const contextData = {
    username: username,
    authTokens: authTokens,
    loginUser: loginUser,
    logoutUser: logoutUser,
  };

  useEffect(() => {
    if (loading) updateToken();
    const fourMinutes = 4 * 60 * 1000;
    const interval = setInterval(() => {
      if (authTokens) {
        updateToken();
      }
    }, fourMinutes);
    return () => clearInterval(interval);
  }, [authTokens, loading]);

  return (
    <AuthContext.Provider value={contextData}>
      {loading ? null : children}
    </AuthContext.Provider>
  );
};

如果你看到logoutUser功能,在那里导航的页面,总是重定向到这个页面,如果用户是未经授权的.

我想限制只发布页面,这就是为什么我使用PrivateRoute.其他页面将向所有人开放.

我被困了一段时间,想不出问题所在.

推荐答案

AuthProvider有一个useEffect钩子,它在发出更新令牌的POST请求的间隔内调用updateToken,当/如果有非200响应状态时,logoutUser函数被调用,并重置一些身份验证状态并导航到"/".这全部发生在用户当前身份验证状态的105处.

您只想在用户实际登录时拨打logoutUser.因为updateToken是从闭包中调用的,所以您可以使用Reaction引用来缓存当前的username状态值,以避免将username作为useEffect依赖项包括在内.

const [username, setUsername] = useState(() =>
  localStorage.getItem("authTokens")
    ? jwt_decode(localStorage.getItem("authTokens"))
    : null
);

// React ref to store cached username "auth" state
const usernameRef = React.useRef(username);

// side-effect to cache username state value
useEffect(() => {
  usernameRef.current = username;
}, [username]);
const updateToken = async () => {
  const response = await fetch(
    "http://127.0.0.1:8000/api/token/refresh/",
    {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
      },
      body: JSON.stringify({ refresh: authTokens?.refresh }),
    }
  );
  let data = await response.json();
  if (response.status === 200) {
    setAuthTokens(data);
    setUsername(jwt_decode(data.access));
    localStorage.setItem("authTokens", JSON.stringify(data));
  } else {
    // If user is currently authenticated, log them out
    if (usernameRef.current) {
      logoutUser();
    }
  }

  if (loading) {
    setLoading(false);
  }
};

Reactjs相关问答推荐

如何使用React Router创建响应式主详细信息应用程序?

在迁移到Redux—Toolkit 2.0和Redux5.0之后,我在extraReducer和Slice方面有点挣扎,

Next.js-图像组件加载问题

如何在与AntD的react 中限制文件上传和显示消息?

React:关于useEffect钩子如何工作的困惑

悬停轴时重新绘制自定义参照线

无法在下一个标头上使用 React hooks

在 React 中,为什么不将组件和钩子结合起来呢?

React 错误无法读取未定义的属性(读取id)#react

.populate() 方法在 mongodb 中不起作用

React-router动态路径

如何将 npm 包添加到我的 Vite + React 应用程序中?

如何渲染子路径并触发react 路由父加载器?

即使配置了 webpack.config.js,html-loader 也不起作用

当我在 useEffect 中使用 useDispatch 时,我的组件继续渲染

将 ref 赋予功能组件以使用内部功能

响应式媒体 React Tailwind

如何从 extrareducer redux 更改对象中元素的值

为什么 React 会多次调用我的应用程序的某些函数?

为什么我不能使用 formik 更改此嵌套对象中的值