Home > Blockchain >  React-Router-Dom 6 - How to dynamically render a component?
React-Router-Dom 6 - How to dynamically render a component?

Time:03-14

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:

Functions are not valid as a React child. This may happen if you return a Component instead of from render

Apparently the new API simply expects:

          <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:

React.jsx: type is invalid -- expected a string (for built-in components) or a class/function (for composite components) but got: undefined. You likely forgot to export your component from the file it's defined in, or you might have mixed up default and named imports.

CodePudding user response:

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:

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

CodePudding user response:

The element prop expects a ReactNode (a.k.a. JSX) and not javascript (i.e. the if-statement).

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. Instead of rendering the children prop it renders an Outlet for nested routes to be rendered into.

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.

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