简体   繁体   中英

How to make this code compatible for react-router v6

In ProtectedRoute.js I have coded:

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;

and in App.js I have written as:

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;

Error says: [ProtectedRoute] is not a Route component. All component children of Routes must be a Route or <React.Fragment>.

Is there something missing! Thank you

In react-router-dom custom route components are no longer used. Routes components can have only Route and React.Fragment components as children, and Route components can have only Routes or other Route components as a parent.

Instead wrapper components handle the business logic and either render the children prop or an Outlet for nested Route components, or the Navigate for redirection.

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

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

The benefit of using the Outlet is you can use a single auth wrapper component and render any number of nested Route children into them, whereas with the children method you cannot render nested routes unless you wrap them in a Routes component.

How about flipping the logic around and checking everything inside ProtectedRoute like this?

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;

And

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;

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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