【问题标题】:React-Router-Dom 6 - How to dynamically render a component?React-Router-Dom 6 - 如何动态渲染组件?
【发布时间】:2023-01-15 02:37:24
【问题描述】:

我的旧方法:

          <Route
            key={i}
            path={path}
            render={(props) => {
              if (!localStorage.getItem("token")) {
                <Redirect
                to={{ pathname: "/login", state: { from: props.location } }}
                />
              }
              return (
                <AuthLayout>
                  <Component {...props} />
                </AuthLayout>
              );
            }}
          />

用新的 element 替换 render 给我:

函数作为 React 子项无效。如果您返回一个组件而不是从渲染中返回,则可能会发生这种情况

显然,新的 API 只是期望:

          <Route
            key={i}
            path={path}
            element={
                <Component />
            }
          />

我真正想要完成的是动态渲染组件:

        {authProtectedRoutes.map(({ path, Component }, i) => {
          <Route
            key={i}
            path={path}
            element={
              // If no auth token, redirect to login
              if (!token) {
                <Navigate to="/login" />
              } else {
                <AuthLayout>
                  <Component />
                </AuthLayout>
              }
            }
          />
        })}

不知道该怎么做...

编辑:

我的组件数组是这样的:

const authProtectedRoutes = [
  { path: "/dashboard", Component: Dashboard },
  { path: "/pages-starter", Component: StarterPage },

当我尝试在循环中返回 Component 时,我得到:

React.jsx:类型无效——需要一个字符串(用于内置 组件)或类/函数(对于复合组件)但得到: 不明确的。您可能忘记从文件中导出您的组件 它在中定义,或者您可能混淆了默认导入和命名导入。

【问题讨论】:

    标签: reactjs react-router-dom


    【解决方案1】:
    element={
      // If no auth token, redirect to login
      if (!token) {
        <Navigate to="/login" />
      } else {
        <AuthLayout>
          <Component />
        </AuthLayout>
      }
    }
    

    你不能在 jsx 中间做一个 if,但是你可以做一个条件运算符:

    element={!token ? (
      <Navigate to="/login" />
    ) : (
      <AuthLayout>
        <Component />
      </AuthLayout>
    )}
    

    【讨论】:

    • 谢谢,这是一步。你能看到我的编辑吗?我做错了其他事情
    • 如错误消息所述,这可能是您导入和导出组件的方式存在问题。仔细检查您是否正确导入了它们。例如,如果 Dashboard 作为默认导出导出,则通过 import Dashboard from 'path/to/file' 导入它,或者如果它是命名导出,则通过 import { Dashboard } from 'path/to/file' 导入它。如果您不确定是哪个组件导致了问题,请尝试添加 console.log 语句(例如,console.log(Dashboard, StarterPage)
    • 该组件已正确导入,只是我通过循环中的对象属性传递它并且不知道如何呈现它。
    【解决方案2】:

    element 道具需要一个 ReactNode (又名 JSX) 而不是 javascript (即 if 语句).

    由于您似乎批量呈现经过身份验证的路由,因此更好的解决方案是将它们全部包装在一个检查令牌的 AuthLayout 组件中。它不是渲染 children 道具,而是渲染一个 Outlet 来渲染嵌套路由。

    例子:

    const AuthLayout = ({ token }) => {
      // ... existing AuthLayout logic
    
      return token
        ? (
          <div /* awesome auth layout CSS style */>
            ...
            <Outlet /> // <-- nested routes render here
          </div>
        )
        : <Navigate to="/login" />;
    };
    

    不要忘记从地图回调中返回 Route

    <Route element={<AuthLayout token={token} />}>
      {authProtectedRoutes.map(({ path, Component }) => (
        <Route key={path} path={path} element={<Component />} />
      ))}
    </Route>
    

    【讨论】:

      【解决方案3】:

      很好的路由相关问题。首先,我从 react-router-dom 的 github 上找到了有用的代码示例:https://github.com/remix-run/react-router/blob/2cd8266765925f8e4651d7caf42ebe60ec8e163a/examples/auth/src/App.tsx#L104

      在这里,作者建议实现额外的 RequireAuth 组件并在路由设置中使用它,而不是将一些逻辑放在“元素”或“渲染”中,如下所示:

      <Route
        path="/protected"
        element={
          <RequireAuth>
            <SomePageComponent />
          </RequireAuth>
        }
      ....
      

      这种方法将允许将与身份验证相关的检查封装在这个新的 RequireAuth 组件中,从而使您的应用程序路由设置更加“轻量级”

      作为一个“简短”示例,我创建了以下您可以参考的代码:

      function App() {
          return (
              <BrowserRouter>
                  <AppRoutes />
              </BrowserRouter>
          );
      }
      
      
      const RequireAuth = ({ children }) => {
          const token = localStorage.getItem('token');
          const currentUrl = useHref();
          return !token ? (
              <Navigate to={`/login?redirect=${currentUrl}`} />
          ) : (
              children
          );
      };
      
      const authProtectedRoutes = [
          { path: '/', component: PaymentsPage },
          { path: '/user', component: UserInfoPage },
      ];
      
      const AppRoutes = () => (
          <Routes>
              {authProtectedRoutes.map((r) => (
                  <Route
                      key={r.path}
                      path={r.path}
                      element={
                          <RequireAuth>
                              <AuthLayout>
                                  <r.component />
                              </AuthLayout>
                          </RequireAuth>
                      }
                  />
              ))}
              <Route path="/login" element={<LoginPage />} />
              <Route path="*" element={<NotFoundPage />} />
          </Routes>
      );
      

      【讨论】: