简体   繁体   English

React-Router-Dom 6 - 如何动态渲染组件?

[英]React-Router-Dom 6 - How to dynamically render a component?

My old method:我的旧方法:

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

Replacing render with the new element gives me:用新element替换render给我:

Functions are not valid as a React child.函数作为 React 子项无效。 This may happen if you return a Component instead of from render如果您返回一个组件而不是从渲染中返回,则可能会发生这种情况

Apparently the new API simply expects:显然,新的 API 只是期望:

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

What I'm really trying to accomplish is to dynamically render the component as such:我真正想要完成的是动态渲染组件:

        {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>
              }
            }
          />
        })}

Not sure how to do this...不知道该怎么做...

EDIT:编辑:

My array of components is as such:我的组件数组是这样的:

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

When I try to return Component in my loop I get:当我尝试在循环中返回Component时,我得到:

React.jsx: type is invalid -- expected a string (for built-in components) or a class/function (for composite components) but got: undefined. React.jsx:类型无效——需要一个字符串(对于内置组件)或一个类/函数(对于复合组件)但得到:未定义。 You likely forgot to export your component from the file it's defined in, or you might have mixed up default and named imports.您可能忘记从定义它的文件中导出您的组件,或者您可能混淆了默认导入和命名导入。

element={
  // If no auth token, redirect to login
  if (!token) {
    <Navigate to="/login" />
  } else {
    <AuthLayout>
      <Component />
    </AuthLayout>
  }
}

You can't do an if in the middle of jsx, but you can do a conditional operator:你不能在 jsx 中间做一个if ,但你可以做一个条件运算符:

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

The element prop expects a ReactNode ( aka JSX ) and not javascript ( ie the if-statement ). element属性需要一个ReactNode又名 JSX )而不是 javascript(即 if 语句)。

Since it seems you render your authenticated routes in bulk a more optimal solution would be to wrap them all in a single AuthLayout component that checks the token.由于您似乎批量呈现经过身份验证的路由,因此更好的解决方案是将它们全部包装在一个检查令牌的AuthLayout组件中。 Instead of rendering the children prop it renders an Outlet for nested routes to be rendered into.它没有渲染children道具,而是渲染了一个用于渲染嵌套路由的Outlet

Example:例子:

const AuthLayout = ({ token }) => {
  // ... existing AuthLayout logic

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

Don't forget to return the Route from the map callback.不要忘记从 map 回调返回Route

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

Nice routing-related question.很好的路由相关问题。 First of all, I found useful code example from react-router-dom's github: https://github.com/remix-run/react-router/blob/2cd8266765925f8e4651d7caf42ebe60ec8e163a/examples/auth/src/App.tsx#L104首先,我从 react-router-dom 的 github 找到了有用的代码示例: https://github.com/remix-run/react-router/blob/2cd8266765925f8e4651d7caf42ebe60ec8e163a/examples/auth/src/App.tsx#L104#L104

Here, instead of putting some logics inside "element" or "render" authors suggest to implement additional RequireAuth component and use it in routing setup like following:在这里,作者建议实现额外的 RequireAuth 组件并在路由设置中使用它,而不是将一些逻辑放在“元素”或“渲染”中,如下所示:

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

This approach would allow to incapsulate auth-related checks inside this new RequireAuth component and as a result make your application routing setup more "lightweight"这种方法将允许将与身份验证相关的检查封装在这个新的 RequireAuth 组件中,从而使您的应用程序路由设置更加“轻量级”

As a "brief" example, I created following piece of code you could reference to:作为一个“简短”示例,我创建了以下您可以参考的代码:

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>
);

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM