如何使此代码与 react-router v6 兼容

Posted

技术标签:

【中文标题】如何使此代码与 react-router v6 兼容【英文标题】:How to make this code compatible for react-router v6 【发布时间】:2022-01-22 21:45:42 【问题描述】:

在 ProtectedRoute.js 中我编写了代码:

const ProtectedRoute = ( component: Component, ...rest ) => 
  const  loading, isAuthenticated, user  = useSelector((state) => state.user);
  return (
    <Fragment>
      !loading && (
          <Routes>
        <Route
          ...rest
          render=(props) => 
            if (!isAuthenticated) 
              return <Navigate to="/login" />;
            
            return <Component ...props />;
          
        />
        </Routes>
      )
    </Fragment>
  );
;

export default ProtectedRoute;

在 App.js 中我写成:

function App() 
  const  isAuthenticated, user  = useSelector((state) => state.user);
  useEffect(() => 
    WebFont.load(
      google:  families: ["Roboto", "Droid Sans", "Chilanka"] ,
    );
    store.dispatch(loadUser());
  , []);

  return (
    <Router>
      <Header />
      isAuthenticated && <UserOptions user=user />
      <Routes>
        <Route exact path="/" element=<Home /> />
        <Route exact path="/product/:id" element=<ProductDetails /> />
        <Route exact path="/products" element=<Products /> />
        <Route path="/products/:keyword" element=<Products /> />
        <Route exact path="/search" element=<Search /> />
        <Route exact path="/login" element=<Authenticate /> />
        <ProtectedRoute exact path="/account" element=<Profile /> />
      </Routes>
      <Footer />
    </Router>
  );


export default App;

错误提示:[ProtectedRoute] 不是 Route 组件。 Routes 的所有子组件必须是 Route 或

是不是少了什么!谢谢

【问题讨论】:

【参考方案1】:

react-router-dom 中不再使用自定义路由组件。 Routes 组件只能有 RouteReact.Fragment 组件作为子组件,Route 组件只能有 Routes 或其他 Route 组件作为父组件。

相反,包装器组件处理业务逻辑,并为嵌套的Route 组件呈现children 属性或Outlet,或为重定向呈现Navigate

渲染children

const ProtectedRoute = ( children ) => 
  const  loading, isAuthenticated, user  = useSelector((state) => state.user);

  if (loading) return null;

  return isAuthenticated
    ? children
    : <Navigate to="/login" replace />;
;

...

<Route
  path="/account"
  element=(
    <ProtectedRoute>
      <Profile />
    </ProtectedRoute>
  )
/>

渲染Outlet

import  Outlet  from 'react-router-dom';

const ProtectedRoute = () => 
  const  loading, isAuthenticated, user  = useSelector((state) => state.user);

  if (loading) return null;

  return isAuthenticated
    ? <Outlet />
    : <Navigate to="/login" replace />;
;

...

<Route path="/account" element=<ProtectedRoute />>
  <Route path="/account" element=<Profile /> />
</Route>

使用Outlet 的好处是您可以使用单个身份验证包装器组件并将任意数量的嵌套Route 子级渲染到其中,而使用children 方法您无法渲染嵌套路由,除非您将它们包装在Routes 组件。

【讨论】:

每次刷新页面时它都会推送到主页。我可以添加哪些更改,以便在刷新页面时它保持在同一页面上?? @MayankPandey 通常,您使用加载或“挂起”状态不提交渲染路由组件或重定向,直到应用程序确定任何身份验证状态。重新加载页面/应用时loadingisAuthenticated 的值是多少? 如果用户已登录(isAuthenticated),它会将页面推送到主页并将加载设置为 false。 @MayankPandey 什么正在导航到您的主页"/" 已认证【参考方案2】:

如何翻转逻辑并像这样检查 ProtectedRoute 中的所有内容?

const ProtectedRoute = () => 
  const  loading, isAuthenticated, user  = useSelector((state) => state.user);
  
  if (loading)  return null; 

  if (!isAuthenticated) 
    return <Navigate to="/login" />;
  

  return <Profile user=user andWhateverElse=true />;
;

export default ProtectedRoute;

function App() 
  const  isAuthenticated, user  = useSelector((state) => state.user);
  useEffect(() => 
    WebFont.load(
      google:  families: ["Roboto", "Droid Sans", "Chilanka"] ,
    );
    store.dispatch(loadUser());
  , []);

  return (
    <Router>
      <Header />
      isAuthenticated && <UserOptions user=user />
      <Routes>
        <Route exact path="/" element=<Home /> />
        <Route exact path="/product/:id" element=<ProductDetails /> />
        <Route exact path="/products" element=<Products /> />
        <Route path="/products/:keyword" element=<Products /> />
        <Route exact path="/search" element=<Search /> />
        <Route exact path="/login" element=<Authenticate /> />
        <Route exact path="/account" element=<ProtectedRoute /> />
      </Routes>
      <Footer />
    </Router>
  );


export default App;

【讨论】:

以上是关于如何使此代码与 react-router v6 兼容的主要内容,如果未能解决你的问题,请参考以下文章

React-router v6 window.scrollTo 不起作用

react-router v6对比react-router v5

React-router v6 该怎么用?

react-router v6新特性总结

react-router v6新特性总结

2021 react-router v6 快速入门