如何避免 React Router 私有路由中的无限循环?

Posted

技术标签:

【中文标题】如何避免 React Router 私有路由中的无限循环?【英文标题】:How can I avoid infinite loops in my React Router private routes? 【发布时间】:2021-07-25 10:08:21 【问题描述】:

在我的 App.js 中,我有一些通过 <PrivateRoute> 保护的经过身份验证的页面,如下所示:

<PrivateRoute path="/dashboard">
  <Dashboard />
</PrivateRoute>

我像这样实现&lt;PrivateRoute&gt;

function PrivateRoute( children, ...rest ) 
  return (
    <Route ...rest render=() => <CheckRedirect children=children /> />
  );

问题是,&lt;CheckRedirect&gt; 函数调用了我服务器上的一个端点,该端点动态地告诉你重定向到哪里

函数如下:

export const CheckRedirect = ( children ) => 
  const [isChecking, setIsChecking] = React.useState(true);
  const [target, setTarget] = React.useState(null);

  const url = "https://example.com/get-redirect"

  useEffect(() =>
    async function getPage() 
      axios.get(url)
      .then(function (response) 
        setTarget(response.data.message)
      ).finally(() => setIsChecking(false))
    
    
    getPage();  
  , []);

  if (isChecking) 
    return "... Checking";
  

  return target ? (
    <Redirect to=target />
  ) : (
    <Redirect to='/404' />
  );
;

如果您没有登录,它会在message 字段中发回“/login”。如果您已登录,它将发送“/dashboard”。

如果它发回“/dashboard”,那么 React Router会产生一个无限循环! 它会再次尝试相同的&lt;PrivateRoute&gt;,它会再次调用端点,它会再次返回“ /dashboard”等等……

如果这已经是重定向的结果,我有没有办法告诉我的&lt;PrivateRoute&gt;执行&lt;CheckRedirect&gt; 函数?

【问题讨论】:

我很确定您应该能够将当前路由名称从PrivateRoute 发送到您的CheckRedirect 函数,并且只有在您的getPage fetch 中执行setTarget ,如果它返回不同的路线。 @benomatis 你能给我举个例子吗?如果我不做setTarget,那么它将在底部返回switch 语句的&lt;Redirect to='/404' /&gt; 部分。我需要 PrivateRoute 以某种方式永远不会调用 CheckRedirect 函数 【参考方案1】:

我自己还没有测试过,但是您是否尝试过将path 作为道具传递给CheckRedirect,并且只在getPage 中提取setTarget,如果它返回不同的路线?

function PrivateRoute( children, path, ...rest ) 
  return (
    <Route ...rest render=() => <CheckRedirect children=children path=path /> />
  );

export const CheckRedirect = ( children, path ) => 
  const [isChecking, setIsChecking] = React.useState(true);
  const [target, setTarget] = React.useState(null);

  const url = "https://example.com/get-redirect"

  useEffect(() =>
    async function getPage() 
      axios.get(url)
      .then(function (response) 
        const newPath = response.data.message
        if (path !== newPath) 
          setTarget(newPath)
        
      ).finally(() => setIsChecking(false))
    
    
    getPage();  
  , []);

  if (isChecking) 
    return "... Checking";
  

  return target ? (
    <Redirect to=target />
  ) : (
    <Redirect to='/404' />
  );
;

为避免CheckRedirect 在一切正常的情况下进行任何重定向(即,这是对该路由的有效请求),请确保CheckRedirect 在这种情况下实际上返回null。如果您可以控制服务器响应,我会为不存在的路由(即重定向到 404)返回一个不同的值(不是null,而是-1),并在何时保留null你真的只想返回null

【讨论】:

如果我不调用setTarget,该函数将返回底部的&lt;Redirect to='/404' /&gt; 行。我认为我们需要PrivateRoute 从不调用 CheckRedirect 函数 你应该可以只返回 null,这应该可以,我想......我会更新我的答案 或者children 您是否可以控制从服务器返回的响应?如果你这样做了,那么我只需在那里更改 return 语句并确保它返回特定于触发器 404 的内容,即。 -1 而不是null,并保持null 只返回null,以防你不想要重定向......将返回null(只是简单的null,没有&lt;Redirect&gt; 组件)在CheckRedirect工作,你能试试吗? 返回null 只是将我重定向回主页/,而不是呈现仪表板组件。我不确定为什么。我不认为服务器响应是这里的问题,而是我们在客户端的处理方式。【参考方案2】:

CheckRedirect 组件中,您甚至不使用 children 道具。它呈现一个字符串,然后重定向到一个页面。它永远循环是正常的。将 path 作为 props 传递给 CheckRedirect 组件,如果它与服务器响应相同,则渲染子组件。

添加路径属性并传递它:

  export const CheckRedirect = ( children, path ) => 

在重定向之前添加条件:

  if (target === path) 
    return children
  

【讨论】:

不幸的是,这并没有解决我有 不同 PrivateRoute 的情况,例如:&lt;PrivateRoute path="/account"&gt; &lt;OtherComponent&gt; &lt;/PrivateRoute&gt; 如果用户去/account,然后CheckRedirect函数返回/dashboard,那么无限循环再次发生(因为在这种情况下path不等于target )【参考方案3】:

只需将您的 PrivateRoute Logic 更改为类似的内容

            const PrivateRoute = ( component: Component, ...rest ) => 
               
               return (
                  <Route
                     ...rest

                     render =  props =>

                        user.isOnline ? ( <Component ...props /> ) :
                        ( 
                           <Redirect
                              to=
                                 pathname: "/login",
                                 state:  from: props.location 
                              
                           />
                        )
                     
                  />
               )
            

然后

            <PrivateRoute exact path="/dashboard" component=Dashboard />

【讨论】:

以上是关于如何避免 React Router 私有路由中的无限循环?的主要内容,如果未能解决你的问题,请参考以下文章

将具有参数的无状态React组件转换为有状态

(通用 React + redux + react-router)如何避免在初始浏览器加载时重新获取路由数据?

如何使用 Apollo 和 React Router 避免嵌套路由/查询组件的请求瀑布?

从cookie异步加载身份验证令牌时,React react-router-dom私有路由不起作用

如何使用嵌套路由和私有路由组件在 react.js 中正确构建路由?

为啥 react-router-dom 自定义路由不起作用?