The React-Router V6 stable version has been released recently, bringing a disruptive API compared to the last two major versions (V5 and V4, with no disruptive changes), and v6 routing capabilities are much more powerful with a series of hooks, which we got a look at first.

  1. Routes to replace the Switch

    In v5, Switch is used to match components that match routes

    // v5
    function App() {
      return (
        <BrowserRouter>
          <Switch>
            <Route exact path="/">
              <Home />
            </Route>
            <Route path="/about">
              <About />
            </Route>
            <Route path="/users/:id" children={<User />} / ></Switch>
        </BrowserRouter>
      );
    }
    Copy the code

    In V5, the Switch plays the core role of route matching by iterating through its child elements, and the V5-based route matching algorithm renders the first component that hits the route.

    // v6
    function App() {
       return (
         <BrowserRouter>
           <Routes>// Replace the Switch with Routes<Route path="/Home" element={<Home />} / ><Route path="/user" element={<Users />} / ></Routes>
         </BrowserRouter>)}Copy the code

    V6 uses Routes to replace the Switch component, which is semantically closer to its subelement Route. A new Route lookup algorithm is implemented in Routes, and many new features are implemented based on this algorithm.

  2. Specification of Route render properties

    V5 provides component rendering, Component rendering, and children rendering

    // v5
    function App() {
      return (
        <BrowserRouter>
          <Switch>/ / component rendering<Route exact path=":userId" component={User} />
            <Route 
              path=":userId" 
              // renderFunction to renderrender={routeProps= > (
                <User routeProps={routeProps} animate={true} />)} / ><Route
              path=":userId"
              // childrenApply colours to a drawingchildren={({ match}) = > (
                match ? (
                  <User match={match} animate={true} />
                ) : (
                  <NotFound />
                )
              )}
            />
            <Route
              path=":userId"
              children={<User animate={true} />} / ></Switch>
        </BrowserRouter>
      );
    }
    Copy the code

    V5’s Route can accept Component as a render component, which looks simple, but we can’t pass custom attributes to the component (User), so we can use Render to compensate for component’s weakness. In addition, V5 also provides the children property. When children is a function, it receives context data matching the route, which is essentially the same function as Render. The v6 version normalized the three attributes of Route into element.

    // v6
    function App() {
       return (
         <BrowserRouter>
           <Routes>
              <Route path="/user" element={<User />} / ><Route path="/user/1" element={<User animate={true} />} / ></Routes>
         </BrowserRouter>)}// In conjunction with hooks provided in v6, you can obtain routing data
    function User({ animate }) {
      let params = useParams();
      let location = useLocation();
    }
    Copy the code

    Element is of type react. ReactNode and can be passed to Suspense for lazy route loading. In addition to supporting the original routing function, V6 standardizes and reduces the component API to reduce the burden of using the developer, and ultimately improves the development experience.

  3. Manual sorting vs intelligent matching

    Since the routing algorithm in V5 renders the first hit routing component, developers need to manually sort it to show component priority when using it. The routing matching algorithm in V6 is more intelligent and powerful, and returns the routing component with higher priority through calculation and comparison. Look at the difference between them with an example.

        <Route path="/user/:id" component={User} />
        <Route path="/user/new" component={NewUser} />
    Copy the code

    We defined two routes, and there is actually no great ambiguity in accessing most /user paths, but when the page accesses /user/new and hits both routes, which component will be rendered? In V5, the final rendering is the first defined routing component (User), that is, define first, render first (as described in the PREVIOUS V5 routing algorithm returns the first matching component), so the developer needs to manually sort the routes to control the priority of component rendering. But this didn’t fit our page rendering expectations. In V6, everything is much more automatic and intelligent. Instead of manually maintaining the order of routing components, the v6 routing matching algorithm automatically selects the rendering. To put it simply, in V6, each path will be divided and each part of the path will be ranked by cumulative scoring. The higher the score is, the rendering will be given priority. The scoring method is as follows:

    // Matches the dynamic part of the route, such as /: ID
    const paramRe = /^:\w+$/;
    // Dynamic routing partial score
    const dynamicSegmentValue = 3;
    // index Child routing score
    const indexRouteValue = 2;
    // Empty routing part score
    const emptySegmentValue = 1;
    // Static route partial score
    const staticSegmentValue = 10;
    // If * exists in the path
    const splatPenalty = -2;
    const isSplat = (s: string) = > s === "*";
    ​
    function computeScore(path: string, index: boolean | undefined) :number {
      // Split paths into arrays
      let segments = path.split("/");
      let initialScore = segments.length;
      if (segments.some(isSplat)) {
        initialScore += splatPenalty;
      }
    ​
     if (index) {
       initialScore += indexRouteValue;
     }
    ​
     return segments
       .filter(s= >! isSplat(s)) .reduce((score, segment) = >
           score +
           (paramRe.test(segment)
             ? dynamicSegmentValue
             : segment === ""
             ? emptySegmentValue
             : staticSegmentValue),
         initialScore
       );
    }
    Copy the code

    Observing this method, it can be seen that the more dynamic a route is, the lower its score is. For example, when there is * in a path, its initial score is relatively low, and the dynamicSegmentValue (3) of dynamic route is higher than the staticSegmentValue (3) of static route. 10) It is much lower. Returning to the example above, v6 calculates /user/:id and /user/new routing scores when accessing the /user/new path. Since /user/new is a static route score higher than /user/:id, v6 renders the NewUser component.

  4. The Link and Route V5 nested paths of relative paths need to concatenate absolute paths to render a component’s child routes, for example

    // v5
    function App() {
     return (
       <BrowserRouter>
         <Switch>
           <Route exact path="/">
             <Home />
           </Route>
           <Route path="/users">
             <Users />
           </Route>
         </Switch>
       </BrowserRouter>
     );
    }
    ​
    function Users() {
      // When a subcomponent renders a nested pattern, it needs to get a match object from useRouteMatch. The developer splashes absolute paths based on the match object for Link and Route components to use.
      let match = useRouteMatch();
    ​
      return (
        <div>
          <nav>
            <Link to={` ${match.url} /me`} >My Profile</Link>
          </nav>
    ​
          <Switch>
            <Route path={` ${match.path} /me`} >
              <OwnUserProfile />
            </Route>
            <Route path={` ${match.path} /:id`} >
              <UserProfile />
            </Route>
          </Switch>
        </div>
      );
    }
    Copy the code

    In V6, when using Link or Route, developers only need to define the relative path of the relative parent Route, and v6 will automatically concatenate the full path for us.

    // v6
    function App() {
      return (
        <BrowserRouter>
          <Routes>
            <Route path="/" element={<Home />} / ><Route path="users/*" element={<Users />} / ></Routes>
        </BrowserRouter>
      );
    }
    
    function Users() {
      return (
        <div>
          <nav>
            <Link to="me">My Profile</Link>
          </nav>
    
          <Routes>
            <Route path=":id" element={<UserProfile />} / ><Route path="me" element={<OwnUserProfile />} / ></Routes>
        </div>
      );
    }
    Copy the code
  5. Outlet component

    V6 brings Outlet components that render subrouting components under the current route. Let’s convert the v6 code in point 4:

    // v6
    function App() {
      return (
        <BrowserRouter>
          <Routes>
            <Route path="/" element={<Home />} / ><Route path="users" element={<Users />} ><Route path="me" element={<OwnUserProfile />} / ><Route path=":id" element={<UserProfile />} / ></Route>
          </Routes>
        </BrowserRouter>
      );
    }
    ​
    function Users() {
      return (
        <div>
          <nav>
            <Link to="me">My Profile</Link>
          </nav>// Child routes are rendered when accessing /users/me or /users/id<Outlet />
        </div>
      );
    }
    Copy the code

    All routes (nested subroutes) can be merged together through outlets, which enables unified management of routes and increases code maintainability.

  6. A series of Hooks

    In fact, V5 provides several hooks, such as useHistory, useLocation, and useRouteMatch, but the core components of V5 are based on class components. The React library in the community is starting to migrate to Hooks, so Hooks in V5 are equivalent to transitioning. V6 has a complete rewrite that embraces Hooks functions and TS. Many apis also use composition to split and wrap code, such as Routes for useRoutes, Navigate for useNavigate, Outlet for useOutlet.

conclusion

The React Router is currently the best solution for Routing management in the React application (none). The v6 version is based on a new routing algorithm that brings powerful features and hooks. The development experience is greatly enhanced 👋