React Router:注销后保留先前的路由而不使用 LocalStorage

问题描述 投票:0回答:1

说明: 我正在开发一个 React 项目,在该项目中,即使用户注销后,我也需要保留用户之前所在的路线,而不使用 localStorage。我使用 React Router v6 和 Apollo Client 进行状态管理和 GraphQL 查询。目前,当用户注销时,之前的路由将重置为默认值(“/”),而不是保留上次访问的路由。

问题: 当用户注销时, previousRouteRef 会重置为默认值(“/”),我需要它来保留上次访问的路由。

预期行为: 我期望 previousRouteRef.current 保存最后访问的路由并在用户注销后保持不变。这样,当用户重新登录时,我可以使用存储的路由将他们重定向回注销之前的位置。

观察结果: 我注意到 RoutesWrapper 组件在注销过程中重新渲染了 3 次。我怀疑这种重复渲染可能会导致 previousRouteRef 重置为其默认值(“/”),而不是保留上次访问的路线。

渲染计数: 我添加了一个计数器来跟踪 RoutesWrapper 渲染的次数,并在每次渲染期间记录 previousRouteRef 的值。日志确认 previousRouteRef.current 在重新渲染后重置为默认路由路径。

涉及文件: RoutesWrapper.js 该组件管理路由并检查用户是否登录。它包含 previousRouteRef,应该保留最后访问的路由。

import { useEffect, useRef, useState } from "react";
import LineWobbleLoader from "@/components/customs/helpers/page-loader";
import useAuthRedirect from "@/hooks/use-auth-redirect";
import MainLayout from "@/layout/main-layout";
import PageLayout from "@/layout/page-layout";
import ForgotPassword from "@/pages/auth-form/login/forgot-password-page";
import { Login } from "@/pages/auth-form/login/login";
import ResetPassword from "@/pages/auth-form/login/reset-password";
import { SignUp } from "@/pages/auth-form/signup/signup";
import SignUpVerify from "@/pages/auth-form/signup/signup-verify";
import AddContact from "@/pages/contacts/add-contact";
import Contacts from "@/pages/contacts/contacts";
import Dashboard from "@/pages/dashboard/dashboard";
import MessageTemplate from "@/pages/messages/message-templates";
import SetupPage from "@/pages/setup/setup-page";
import SuccessPage from "@/pages/success/success-page";
import CreateTemplate from "@/pages/templates/add-template";
import { Navigate, Route, Routes } from "react-router-dom";

function RoutesWrapper() {
  const [isLoggedIn, setIsLoggedIn] = useState<boolean>(false);
  const previousRouteRef = useRef<string>("/");
  const renderCount = useRef(0);
  
  const { meData, whatsappData, meLoading, whatsappLoading, refetchMe } =
    useAuthRedirect({ isLoggedIn });

  renderCount.current += 1;
  console.log(`RoutesWrapper render count: ${renderCount.current}`);

  useEffect(() => {
    refetchMe();
  }, [isLoggedIn, refetchMe]);

  if (meLoading || whatsappLoading) {
    return (
      <div className="h-screen flex justify-center items-center">
        <LineWobbleLoader />
      </div>
    );
  }

  return (
    <Routes>
      {!meData ? (
        <>
          <Route path="/" element={<Login setIsLoggedIn={setIsLoggedIn} previousRouteRef={previousRouteRef} />} />
          <Route path="/signup" element={<SignUp />} />
          <Route path="/forgot-password" element={<ForgotPassword />} />
          <Route path="/reset-password" element={<ResetPassword />} />
          <Route path="/success-page" element={<SuccessPage />} />
        </>
      ) : (
        <>
          <Route path="/" element={whatsappData?.getWhatsApp?.business ? <Navigate to="/contacts" replace /> : <Navigate to="/setup" replace />} />
          <Route path="/setup" element={<SetupPage setIsLoggedIn={setIsLoggedIn} previousRouteRef={previousRouteRef} />} />
          <Route element={<PageLayout />}>
            {whatsappData?.getWhatsApp?.business ? (
              <Route element={<MainLayout previousRouteRef={previousRouteRef} meData={meData.me} setIsLoggedIn={setIsLoggedIn} />}>
                <Route path="/dashboard" element={<Dashboard />} />
                <Route path="/contacts" element={<Contacts />} />
                <Route path="/addcontact" element={<AddContact />} />
                <Route path="/templates" element={<CreateTemplate />} />
                <Route path="/newtemplate" element={<MessageTemplate />} />
                <Route path="*" element={<Navigate to="/" replace />} />
              </Route>
            ) : (
              <Route path="*" element={<Navigate to="/setup" replace />} />
            )}
          </Route>
        </>
      )}
    </Routes>
  );
}

export default RoutesWrapper;


useLogout.js 该挂钩处理注销过程,并应在注销之前将 previousRouteRef 设置为当前路径名。

import { useMutation } from "@apollo/client";
import { LOGOUT } from "@/services/gpl/mutations";

const useLogout = ({ setIsLoggedIn, previousRouteRef }) => {
  const [logout] = useMutation(LOGOUT);

  const handleLogout = () => {
    previousRouteRef.current = window.location.pathname; // Persisting the current route
    logout()
      .then(() => {
        setIsLoggedIn(false);
        window.location.href = "/";
      })
      .catch(error => {
        console.error("Logout error:", error);
      });
  };

  return { handleLogout };
};

export default useLogout;

使用登录.tsx

import { useLazyQuery, useApolloClient } from "@apollo/client";
import { LOGIN, ME } from "@/services/gpl/queries";
import { useToast } from "@/components/ui/use-toast";
import { useNavigate } from "react-router-dom";

interface LoginInput {
  email: string;
  password: string;
}

type Props = {
  setIsLoggedIn: React.Dispatch<React.SetStateAction<boolean>>;
  previousRouteRef: React.MutableRefObject<string>;
};

const useLogin = ({ setIsLoggedIn, previousRouteRef }: Props) => {
  const { toast } = useToast();
  const navigate = useNavigate();
  const client = useApolloClient();
  const [login, { loading, error, data }] = useLazyQuery(LOGIN);

  const handleLogin = (loginInput: LoginInput) => {
    login({ variables: { login: loginInput } })
      .then((response) => {
        if (response.data?.login) {
          setIsLoggedIn(true);
          const cachedMeData = client.readQuery({ query: ME });
          if (cachedMeData && cachedMeData.me) {
            if (cachedMeData.me.email === response.data.login.email) {
              navigate(previousRouteRef.current || "/contacts");
              return;
            }
          }
          navigate("/contacts");
        } else {
          toast({
            title: "Failed",
            description: error?.message,
            className: "text-red-600 border-red-600",
          });
        }
      })
      .catch((error) => {
        console.log(error);
        toast({
          description: "Invalid email or password",
          className: "text-red-600 border-red-600",
        });
      });
  };

  return {
    loading,
    error,
    data,
    handleLogin,
  };
};

export default useLogin;

我尝试过的: 使用 useRef 作为 previousRouteRef:我使用 useRef 初始化了 previousRouteRef,以存储注销前最后访问的路由。但是,注销后,该值将重置为默认值(“/”)而不是保留。 在handleLogout中设置previousRouteRef:我尝试在handleLogout函数中注销之前将previousRouteRef.current设置为window.location.pathname。尽管如此,该值仍然重置为默认路由。 调试渲染计数:我添加了控制台日志来跟踪 RoutesWrapper 组件的渲染计数和 previousRouteRef 的值。我观察到该组件在注销过程中多次重新渲染,这可能会影响状态。

reactjs react-hooks react-router apollo-client
1个回答
0
投票

如果您真的想“保留”以前访问的路由 URL,您可能应该使用 localStorage,但我看到的问题是在您的注销处理程序中,您在其中执行硬重定向回

"/"
,即
window.location.href = "/";
,这必然是重新加载页面,从而重新安装整个 React 应用程序。
previousRouteRef
将被初始化为
"/"

我建议使用

useNavigate
钩子并发出客户端重定向回主页。

示例:

import { useLocation, useNavigate } from "react-router-dom";
import { useMutation } from "@apollo/client";
import { LOGOUT } from "@/services/gpl/mutations";

const useLogout = ({ setIsLoggedIn, previousRouteRef }) => {
  const { pathname } = useLocation();
  const navigate = useNavigate();

  const [logout] = useMutation(LOGOUT);

  const handleLogout = async () => {
    previousRouteRef.current = pathname; // Persisting the current route

    try {
      await logout();
      setIsLoggedIn(false);
      navigate("/", { replace: true });
    } catch (error) {
      console.error("Logout error:", error);
    };
  };

  return { handleLogout };
};

export default useLogout;

这应该保持应用程序已安装并且不会重置

previousRouteRef
值。

© www.soinside.com 2019 - 2024. All rights reserved.