DEV Community

Tianya School
Tianya School

Posted on

React Router v6 Latest Advances in Route Management

React Router v6 is a significant update for route management in React applications, introducing improvements and simplifications, including better handling of nested routes and enhanced use of hooks.

1. Routes Refactor

In v6, the <Route> component is wrapped within a <Routes> component, replacing the <Switch> component. <Routes> automatically renders the first matching route.

import { BrowserRouter as Router, Routes, Route } from 'react-router-dom';

function App() {
  return (
    <Router>
      <Routes>
        <Route path="/" element={<Home />} />
        <Route path="/about" element={<About />} />
        <Route path="/users/*" element={<Users />} /> {/* Wildcard support */}
      </Routes>
    </Router>
  );
}
Enter fullscreen mode Exit fullscreen mode

2. Using the element Property

Routes now specify the component to render using the element property, replacing component or render functions.

<Route path="/profile/:userId" element={<Profile />} />
Enter fullscreen mode Exit fullscreen mode

3. Hook API

React Router v6 introduces hooks like useParams, useLocation, and useNavigate, enabling direct navigation handling within components.

import { useParams } from 'react-router-dom';

function Profile() {
  const { userId } = useParams();
  return <div>Profile of user {userId}</div>;
}
Enter fullscreen mode Exit fullscreen mode

4. Navigate Function

The useNavigate hook returns a navigate function for programmatic navigation.

import { useNavigate } from 'react-router-dom';

function ProfileForm() {
  const navigate = useNavigate();
  const handleSubmit = (event) => {
    event.preventDefault();
    // Navigate to another page after form submission
    navigate('/success', { replace: true }); // Replace current history entry
  };

  return <form onSubmit={handleSubmit}>...</form>;
}
Enter fullscreen mode Exit fullscreen mode

5. Lazy Loading and Code Splitting

React Router v6 supports dynamic imports for code splitting, improving load times.

<Route path="/lazy" element={import('./LazyComponent').then((mod) => mod.LazyComponent)} />
Enter fullscreen mode Exit fullscreen mode

6. 404 Page

Handle unmatched routes with a wildcard route to display a 404 page.

<Route path="*" element={<Error404 />} />
Enter fullscreen mode Exit fullscreen mode

7. Nested Routes

Nested routes are simplified using <Routes> and <Route> combinations.

function App() {
  return (
    <Router>
      <Routes>
        <Route path="/" element={<MainLayout />}>
          <Route index element={<Home />} />
          <Route path="about" element={<About />} />
          <Route path="users" element={<Users />}>
            <Route path=":userId" element={<User />} />
          </Route>
        </Route>
        <Route path="*" element={<Error404 />} />
      </Routes>
    </Router>
  );
}
Enter fullscreen mode Exit fullscreen mode

8. Route Protection and Authorization

Use useEffect or useLayoutEffect with useNavigate to protect routes, ensuring only authenticated users access them.

import { useNavigate, useLocation } from 'react-router-dom';

function PrivateRoute({ children }) {
  const location = useLocation();
  const navigate = useNavigate();

  useEffect(() => {
    if (!isAuthenticated()) {
      navigate('/login', { replace: true });
    }
  }, [navigate]);

  return isAuthenticated() ? children : null;
}

function App() {
  return (
    <Router>
      <Routes>
        <Route path="/" element={<PublicRoute><Home /></PublicRoute>} />
        <Route path="/dashboard" element={<PrivateRoute><Dashboard /></PrivateRoute>} />
        <Route path="/login" element={<Login />} />
      </Routes>
    </Router>
  );
}
Enter fullscreen mode Exit fullscreen mode

Here, isAuthenticated() is a hypothetical function checking user authentication. Unauthenticated users are redirected to the login page.

9. Redirects and Navigation

The <Navigate> component facilitates redirects by triggering navigation to a specified URL.

import { Navigate } from 'react-router-dom';

function PrivateRoute({ children }) {
  if (!isAuthenticated()) {
    return <Navigate to="/login" replace />;
  }

  return children;
}
Enter fullscreen mode Exit fullscreen mode

10. Route Parameters and Query Strings

Use useParams for route parameters and useLocation for query strings.

import { useParams, useLocation } from 'react-router-dom';

function Profile() {
  const { userId } = useParams();
  const query = new URLSearchParams(useLocation().search);
  const searchParam = query.get('paramName');

  return <div>Profile of user {userId} with search param: {searchParam}</div>;
}
Enter fullscreen mode Exit fullscreen mode

11. Custom Route Matching

React Router v6 supports custom route matching via the path-to-regexp library, though standard path patterns are usually sufficient.

12. Higher-Order Components (HoCs)

While v6 discourages HoCs, you can still wrap components for specific logic, such as route protection.

function PrivateRoute({ children }) {
  if (!isAuthenticated()) {
    return <Navigate to="/login" replace />;
  }

  return <>{children}</>;
}

function App() {
  return (
    <Router>
      <Routes>
        <Route path="/" element={<PublicRoute><Home /></PublicRoute>} />
        <Route path="/dashboard" element={<PrivateRoute><Dashboard /></PrivateRoute>} />
        <Route path="/login" element={<Login />} />
      </Routes>
    </Router>
  );
}
Enter fullscreen mode Exit fullscreen mode

13. Route Events

React Router v6 provides lifecycle hooks like useLocation and useNavigate to monitor and respond to route changes.

import { useLocation } from 'react-router-dom';

function Navbar() {
  const location = useLocation();

  useEffect(() => {
    console.log(`Navigated to ${location.pathname}`);
  }, [location]);

  return /* ... */;
}
Enter fullscreen mode Exit fullscreen mode

14. Modular Route Configuration

Keep code clean by splitting route configurations into modules and merging them in the main configuration.

// routes/users.js
export default [
  { path: '/users', element: <UsersList /> },
  { path: '/users/:id', element: <UserProfile /> },
];

// routes/admin.js
export default [
  { path: '/admin/dashboard', element: <AdminDashboard /> },
  { path: '/admin/users', element: <AdminUsers /> },
];

// App.js
import { BrowserRouter as Router, Routes, Route } from 'react-router-dom';
import usersRoutes from './routes/users';
import adminRoutes from './routes/admin';

function App() {
  return (
    <Router>
      <Routes>
        {usersRoutes.map((route, index) => (
          <Route key={index} {...route} />
        ))}
        {adminRoutes.map((route, index) => (
          <Route key={index} {...route} />
        ))}
        <Route path="*" element={<ErrorPage />} />
      </Routes>
    </Router>
  );
}
Enter fullscreen mode Exit fullscreen mode

15. Route Guards

Create custom guard functions to control route access, often for authorization or data preloading.

function requireAuth(nextState, replace) {
  if (!isAuthenticated()) {
    replace({
      pathname: '/login',
      state: { nextPathname: nextState.location.pathname },
    });
  }
}

// In routes
<Route path="/protected" onEnter={requireAuth} element={<ProtectedComponent />} />
Enter fullscreen mode Exit fullscreen mode

In v6, use useEffect or useLayoutEffect for similar functionality.

16. Nested Routing

Nested routes are handled without explicitly wrapping <Switch>, using nested <Routes>:

function App() {
  return (
    <Router>
      <Routes>
        <Route path="/" element={<MainLayout />}>
          <Route index element={<Home />} />
          <Route path="about" element={<About />} />
          <Route path="users" element={<Users />}>
            <Route path=":userId" element={<User />} />
          </Route>
        </Route>
        <Route path="*" element={<Error404 />} />
      </Routes>
    </Router>
  );
}
Enter fullscreen mode Exit fullscreen mode

Here, /users/:userId is defined within /users, rendering both Users and User components for /users/someId.

17. Custom Error Handling

React Router v6 doesn’t provide global error handling, but you can use window.onerror or custom middleware.

18. Code Splitting and Lazy Loading

React Router v6 integrates with Webpack or other bundlers for code splitting and lazy loading using dynamic import():

<Route
  path="/lazy"
  element={
    import('./LazyComponent')
      .then((mod) => mod.LazyComponent)
      .catch((error) => <ErrorBoundary error={error} />)
  }
/>
Enter fullscreen mode Exit fullscreen mode

19. Custom Route Components

While the <Route> component is simplified, you can create custom components to extend functionality, such as adding loading indicators.

20. Server-Side Rendering (SSR)

React Router v6 supports SSR with additional configuration and libraries like react-router-dom/server and react-router-dom/browser.

🚀 Join my technical exchange group to get daily useful information:

Top comments (0)