Step by step allows you to quickly upgrade your React Router v5 router to v6

Keywords: Programming React github

Thank you for your reference- http://bjbsair.com/2020-04-01/tech-info/18408.html

Pictures from the Internet

1 Introduction

The version of react route V6 alpha has been released. This week, we will analyze the changes through the article a snake peak at react route V6.

React Router v6: https://github.com/ReactTraining/react-router

A Sneak Peek at React Router v6 : https://alligator.io/react/react-router-v6/

2 Overview

Renamed

A painless change makes API naming more standard.

// v5  
import { BrowserRouter, Switch, Route } from "react-router-dom";  
  
function App() {  
  return (  
    <BrowserRouter>  
      <Switch>  
        <Route exact path="/">  
          <Home />  
        </Route>  
        <Route path="/profile">  
          <Profile />  
        </Route>  
      </Switch>  
    </BrowserRouter>  
  );  
}

In the V6 version of react router, directly use Routes instead of Switch:

// v6  
import { BrowserRouter, Routes, Route } from "react-router-dom";  
  
function App() {  
  return (  
    <BrowserRouter>  
      <Routes>  
        <Route path="/" element={<Home />} />  
        <Route path="profile/*" element={<Profile />} />  
      </Routes>  
    </BrowserRouter>  
  );  
}

upgrade

In v5 version, it is not very intuitive to pass parameters to components. You need to use RenderProps to pass through routeProps:

import Profile from './Profile';  
  
// v5  
<Route path=":userId" component={Profile} />  
<Route  
  path=":userId"  
  render={routeProps => (  
    <Profile {...routeProps} animate={true} />  
  )}  
/>  
  
// v6  
<Route path=":userId" element={<Profile />} />  
<Route path=":userId" element={<Profile animate={true} />} />

In v6, the render and component schemes are combined into element schemes, which can easily pass props without passing through the roteProps parameters.

More convenient nested routing

In v5, nested routes need to get match through useRouteMatch, and sub routes can be realized through match.path splicing:

// v5  
import {  
  BrowserRouter,  
  Switch,  
  Route,  
  Link,  
  useRouteMatch  
} from "react-router-dom";  
  
function App() {  
  return (  
    <BrowserRouter>  
      <Switch>  
        <Route exact path="/" component={Home} />  
        <Route path="/profile" component={Profile} />  
      </Switch>  
    </BrowserRouter>  
  );  
}  
  
function Profile() {  
  let match = useRouteMatch();  
  
  return (  
    <div>  
      <nav>  
        <Link to={`${match.url}/me`}>My Profile</Link>  
      </nav>  
  
      <Switch>  
        <Route path={`${match.path}/me`}>  
          <MyProfile />  
        </Route>  
        <Route path={`${match.path}/:id`}>  
          <OthersProfile />  
        </Route>  
      </Switch>  
    </div>  
  );  
}

In the v6 version, the useRouteMatch step is omitted, and the relative path can be directly represented by path:

// v6  
import { BrowserRouter, Routes, Route, Link, Outlet } from "react-router-dom";  
  
// Approach #1  
function App() {  
  return (  
    <BrowserRouter>  
      <Routes>  
        <Route path="/" element={<Home />} />  
        <Route path="profile/*" element={<Profile />} />  
      </Routes>  
    </BrowserRouter>  
  );  
}  
  
function Profile() {  
  return (  
    <div>  
      <nav>  
        <Link to="me">My Profile</Link>  
      </nav>  
  
      <Routes>  
        <Route path="me" element={<MyProfile />} />  
        <Route path=":id" element={<OthersProfile />} />  
      </Routes>  
    </div>  
  );  
}  
  
// Approach #2  
// You can also define all  
// <Route> in a single place  
function App() {  
  return (  
    <BrowserRouter>  
      <Routes>  
        <Route path="/" element={<Home />} />  
        <Route path="profile" element={<Profile />}>  
          <Route path=":id" element={<MyProfile />} />  
          <Route path="me" element={<OthersProfile />} />  
        </Route>  
      </Routes>  
    </BrowserRouter>  
  );  
}  
  
function Profile() {  
  return (  
    <div>  
      <nav>  
        <Link to="me">My Profile</Link>  
      </nav>  
  
      <Outlet />  
    </div>  
  );  
}

Note that the Outlet is the Element of the render sub route.

useNavigate instead of useHistory

In v5, the active jump route can be used for history.push and other operations through useHistory

// v5  
import { useHistory } from "react-router-dom";  
  
function MyButton() {  
  let history = useHistory();  
  function handleClick() {  
    history.push("/home");  
  }  
  return <button onClick={handleClick}>Submit</button>;  
}

In v6, you can directly implement this common operation through useNavigate:

// v6  
import { useNavigate } from "react-router-dom";  
  
function MyButton() {  
  let navigate = useNavigate();  
  function handleClick() {  
    navigate("/home");  
  }  
  return <button onClick={handleClick}>Submit</button>;  
}

The history is encapsulated in the react router. If history.replace is needed, you can specify it with the {replace: true} parameter:

// v5  
history.push("/home");  
history.replace("/home");  
  
// v6  
navigate("/home");  
navigate("/home", { replace: true });

Smaller volume 8kb

Due to the almost reconstruction of the code, the volume of the v6 version is reduced from 20kb to 8kb after compression.

3 intensive reading

There is a core idea in the source code of react router V6. The author will share it with you, which is very helpful for some framework development. Let's take a look at the code excerpt of useRoutes:

export function useRoutes(routes, basename = "", caseSensitive = false) {  
  let {  
    params: parentParams,  
    pathname: parentPathname,  
    route: parentRoute  
  } = React.useContext(RouteContext);  
  
  if (warnAboutMissingTrailingSplatAt) {  
    // ...  
  }  
  
  basename = basename ? joinPaths([parentPathname, basename]) : parentPathname;  
  
  let navigate = useNavigate();  
  let location = useLocation();  
  let matches = React.useMemo(  
    () => matchRoutes(routes, location, basename, caseSensitive),  
    [routes, location, basename, caseSensitive]  
  );  
  
  // ...  
  
  // Otherwise render an element.  
  let element = matches.reduceRight((outlet, { params, pathname, route }) => {  
    return (  
      <RouteContext.Provider  
        children={route.element}  
        value={{  
          outlet,  
          params: readOnly({ ...parentParams, ...params }),  
          pathname: joinPaths([basename, pathname]),  
          route  
        }}  
      />  
    );  
  }, null);  
  
  return element;  
}

As you can see, with React.Context, the v6 version wraps a layer of RouteContext when rendering each routing element.

For more convenient routing nesting:

In v6 version, useRouteMatch is omitted, and path is directly used to represent relative path.

This is achieved by using this scheme. Because each layer of route file is wrapped with Context, the path of the previous layer can be obtained at each layer. Therefore, when splicing routes, it can be completely implemented by the framework, without the need for users to splice them in advance when invoking.

Taking useNavigate as an example, some people think that the encapsulation of navigation only stays in the formal layer, but it also has encapsulation in function. For example, if the incoming path is a relative path, it will switch according to the current route. Here is the excerpt of useNavigate Code:

export function useNavigate() {  
  let { history, pending } = React.useContext(LocationContext);  
  let { pathname } = React.useContext(RouteContext);  
  
  let navigate = React.useCallback(  
    (to, { replace, state } = {}) => {  
      if (typeof to === "number") {  
        history.go(to);  
      } else {  
        let relativeTo = resolveLocation(to, pathname);  
  
        let method = !!replace || pending ? "replace" : "push";  
        history[method](relativeTo, state);  
      }  
    },  
    [history, pending, pathname]  
  );  
  
  return navigate;  
}

You can see that RouteContext is used to get the current pathname, and to and pathname are spliced according to resolveLocation, and pathname is provided through RouteContext.Provider.

Using multi-layer Context Provider skillfully

Many times, we use Context to stay at the level of one Provider and multiple usecontexts, which is the most basic usage of Context. However, we believe that after reading React Router v6, we can find more usage of Context: multi-layer Context Provider.

Although it is said that there are multiple layers of Context Provider that will adopt the principle of recent coverage, this is not only a function to avoid errors, we can use this function to achieve such improvements as React Router v6.

To illustrate this feature in more detail, here is another specific example: for example, when building a rendering engine, each component has an id, but this id does not show up in the props of the component:

const Input = () => {  
  // The Input component will automatically generate an id in the canvas, but this id component cannot be obtained through props  
};

At this time, if we allow another sub element to be created inside the Input component and want the id of this sub element to be derived from Input, we may need the user to do this:

const Input = ({ id }) => {  
  return <ComponentLoader id={id + "1"} />;  
};

There are two problems in doing so:

  1. Exposing the id to the Input component violates the simplicity of the previous design.
  2. The component needs to assemble the id, which is very troublesome.

The problems encountered here are the same as those encountered by React Router. Can we simplify the code as follows, but the function remains the same?

const Input = () => {  
  return <ComponentLoader id="1" />;  
};

The answer is yes, we can use Context to implement this solution. The key point is to render Input, but the component container needs to wrap a Provider:

const ComponentLoader = ({ id, element }) => {  
  <Context.Provider value={{ id }}>{element}</Context.Provider>;  
};

For internal components, the id obtained by calling useContext at different levels is different, which is exactly the effect we want:

const ComponentLoader = ({id,element}) => {  
  const { id: parentId } = useContext(Context)  
  
  <Context.Provider value={{ id: parentId + id }}>  
    {element}  
  </Context.Provider>  
}

In this way, the < componentloader id = "1" / > we call inside Input actually splices the actual ID 01, which is completely thrown to the external engine layer for processing, and the user does not need to manually splice.

4 Summary

After the React Router v6 is fully refactored with Hooks, it not only simplifies the code, but also becomes more useful. When the official version is released, it can be upgraded quickly.

In addition, from these optimizations made by React Router v6, we have discovered more ingenious use of Context from the source code. I hope this method can help you to apply it to other more complex project designs. Thank you for your reference- http://bjbsair.com/2020-04-01/tech-info/18408.html

Pictures from the Internet

1 Introduction

The version of react route V6 alpha has been released. This week, we will analyze the changes through the article a snake peak at react route V6.

React Router v6: https://github.com/ReactTraining/react-router

A Sneak Peek at React Router v6 : https://alligator.io/react/react-router-v6/

2 Overview

Renamed

A painless change makes API naming more standard.

// v5  
import { BrowserRouter, Switch, Route } from "react-router-dom";  
  
function App() {  
  return (  
    <BrowserRouter>  
      <Switch>  
        <Route exact path="/">  
          <Home />  
        </Route>  
        <Route path="/profile">  
          <Profile />  
        </Route>  
      </Switch>  
    </BrowserRouter>  
  );  
}

In the V6 version of react router, directly use Routes instead of Switch:

// v6  
import { BrowserRouter, Routes, Route } from "react-router-dom";  
  
function App() {  
  return (  
    <BrowserRouter>  
      <Routes>  
        <Route path="/" element={<Home />} />  
        <Route path="profile/*" element={<Profile />} />  
      </Routes>  
    </BrowserRouter>  
  );  
}

upgrade

In v5 version, it is not very intuitive to pass parameters to components. You need to use RenderProps to pass through routeProps:

import Profile from './Profile';  
  
// v5  
<Route path=":userId" component={Profile} />  
<Route  
  path=":userId"  
  render={routeProps => (  
    <Profile {...routeProps} animate={true} />  
  )}  
/>  
  
// v6  
<Route path=":userId" element={<Profile />} />  
<Route path=":userId" element={<Profile animate={true} />} />

In v6, the render and component schemes are combined into element schemes, which can easily pass props without passing through the roteProps parameters.

More convenient nested routing

In v5, nested routes need to get match through useRouteMatch, and sub routes can be realized through match.path splicing:

// v5  
import {  
  BrowserRouter,  
  Switch,  
  Route,  
  Link,  
  useRouteMatch  
} from "react-router-dom";  
  
function App() {  
  return (  
    <BrowserRouter>  
      <Switch>  
        <Route exact path="/" component={Home} />  
        <Route path="/profile" component={Profile} />  
      </Switch>  
    </BrowserRouter>  
  );  
}  
  
function Profile() {  
  let match = useRouteMatch();  
  
  return (  
    <div>  
      <nav>  
        <Link to={`${match.url}/me`}>My Profile</Link>  
      </nav>  
  
      <Switch>  
        <Route path={`${match.path}/me`}>  
          <MyProfile />  
        </Route>  
        <Route path={`${match.path}/:id`}>  
          <OthersProfile />  
        </Route>  
      </Switch>  
    </div>  
  );  
}

In the v6 version, the useRouteMatch step is omitted, and the relative path can be directly represented by path:

// v6  
import { BrowserRouter, Routes, Route, Link, Outlet } from "react-router-dom";  
  
// Approach #1  
function App() {  
  return (  
    <BrowserRouter>  
      <Routes>  
        <Route path="/" element={<Home />} />  
        <Route path="profile/*" element={<Profile />} />  
      </Routes>  
    </BrowserRouter>  
  );  
}  
  
function Profile() {  
  return (  
    <div>  
      <nav>  
        <Link to="me">My Profile</Link>  
      </nav>  
  
      <Routes>  
        <Route path="me" element={<MyProfile />} />  
        <Route path=":id" element={<OthersProfile />} />  
      </Routes>  
    </div>  
  );  
}  
  
// Approach #2  
// You can also define all  
// <Route> in a single place  
function App() {  
  return (  
    <BrowserRouter>  
      <Routes>  
        <Route path="/" element={<Home />} />  
        <Route path="profile" element={<Profile />}>  
          <Route path=":id" element={<MyProfile />} />  
          <Route path="me" element={<OthersProfile />} />  
        </Route>  
      </Routes>  
    </BrowserRouter>  
  );  
}  
  
function Profile() {  
  return (  
    <div>  
      <nav>  
        <Link to="me">My Profile</Link>  
      </nav>  
  
      <Outlet />  
    </div>  
  );  
}

Note that the Outlet is the Element of the render sub route.

useNavigate instead of useHistory

In v5, the active jump route can be used for history.push and other operations through useHistory

// v5  
import { useHistory } from "react-router-dom";  
  
function MyButton() {  
  let history = useHistory();  
  function handleClick() {  
    history.push("/home");  
  }  
  return <button onClick={handleClick}>Submit</button>;  
}

In v6, you can directly implement this common operation through useNavigate:

// v6  
import { useNavigate } from "react-router-dom";  
  
function MyButton() {  
  let navigate = useNavigate();  
  function handleClick() {  
    navigate("/home");  
  }  
  return <button onClick={handleClick}>Submit</button>;  
}

The history is encapsulated in the react router. If history.replace is needed, you can specify it with the {replace: true} parameter:

// v5  
history.push("/home");  
history.replace("/home");  
  
// v6  
navigate("/home");  
navigate("/home", { replace: true });

Smaller volume 8kb

Due to the almost reconstruction of the code, the volume of the v6 version is reduced from 20kb to 8kb after compression.

3 intensive reading

There is a core idea in the source code of react router V6. The author will share it with you, which is very helpful for some framework development. Let's take a look at the code excerpt of useRoutes:

export function useRoutes(routes, basename = "", caseSensitive = false) {  
  let {  
    params: parentParams,  
    pathname: parentPathname,  
    route: parentRoute  
  } = React.useContext(RouteContext);  
  
  if (warnAboutMissingTrailingSplatAt) {  
    // ...  
  }  
  
  basename = basename ? joinPaths([parentPathname, basename]) : parentPathname;  
  
  let navigate = useNavigate();  
  let location = useLocation();  
  let matches = React.useMemo(  
    () => matchRoutes(routes, location, basename, caseSensitive),  
    [routes, location, basename, caseSensitive]  
  );  
  
  // ...  
  
  // Otherwise render an element.  
  let element = matches.reduceRight((outlet, { params, pathname, route }) => {  
    return (  
      <RouteContext.Provider  
        children={route.element}  
        value={{  
          outlet,  
          params: readOnly({ ...parentParams, ...params }),  
          pathname: joinPaths([basename, pathname]),  
          route  
        }}  
      />  
    );  
  }, null);  
  
  return element;  
}

As you can see, with React.Context, the v6 version wraps a layer of RouteContext when rendering each routing element.

For more convenient routing nesting:

In v6 version, useRouteMatch is omitted, and path is directly used to represent relative path.

This is achieved by using this scheme. Because each layer of route file is wrapped with Context, the path of the previous layer can be obtained at each layer. Therefore, when splicing routes, it can be completely implemented by the framework, without the need for users to splice them in advance when invoking.

Taking useNavigate as an example, some people think that the encapsulation of navigation only stays in the formal layer, but it also has encapsulation in function. For example, if the incoming path is a relative path, it will switch according to the current route. Here is the excerpt of useNavigate Code:

export function useNavigate() {  
  let { history, pending } = React.useContext(LocationContext);  
  let { pathname } = React.useContext(RouteContext);  
  
  let navigate = React.useCallback(  
    (to, { replace, state } = {}) => {  
      if (typeof to === "number") {  
        history.go(to);  
      } else {  
        let relativeTo = resolveLocation(to, pathname);  
  
        let method = !!replace || pending ? "replace" : "push";  
        history[method](relativeTo, state);  
      }  
    },  
    [history, pending, pathname]  
  );  
  
  return navigate;  
}

You can see that RouteContext is used to get the current pathname, and to and pathname are spliced according to resolveLocation, and pathname is provided through RouteContext.Provider.

Using multi-layer Context Provider skillfully

Many times, we use Context to stay at the level of one Provider and multiple usecontexts, which is the most basic usage of Context. However, we believe that after reading React Router v6, we can find more usage of Context: multi-layer Context Provider.

Although it is said that there are multiple layers of Context Provider that will adopt the principle of recent coverage, this is not only a function to avoid errors, we can use this function to achieve such improvements as React Router v6.

To illustrate this feature in more detail, here is another specific example: for example, when building a rendering engine, each component has an id, but this id does not show up in the props of the component:

const Input = () => {  
  // The Input component will automatically generate an id in the canvas, but this id component cannot be obtained through props  
};

At this time, if we allow another sub element to be created inside the Input component and want the id of this sub element to be derived from Input, we may need the user to do this:

const Input = ({ id }) => {  
  return <ComponentLoader id={id + "1"} />;  
};

There are two problems in doing so:

  1. Exposing the id to the Input component violates the simplicity of the previous design.
  2. The component needs to assemble the id, which is very troublesome.

The problems encountered here are the same as those encountered by React Router. Can we simplify the code as follows, but the function remains the same?

const Input = () => {  
  return <ComponentLoader id="1" />;  
};

The answer is yes, we can use Context to implement this solution. The key point is to render Input, but the component container needs to wrap a Provider:

const ComponentLoader = ({ id, element }) => {  
  <Context.Provider value={{ id }}>{element}</Context.Provider>;  
};

For internal components, the id obtained by calling useContext at different levels is different, which is exactly the effect we want:

const ComponentLoader = ({id,element}) => {  
  const { id: parentId } = useContext(Context)  
  
  <Context.Provider value={{ id: parentId + id }}>  
    {element}  
  </Context.Provider>  
}

In this way, the < componentloader id = "1" / > we call inside Input actually splices the actual ID 01, which is completely thrown to the external engine layer for processing, and the user does not need to manually splice.

4 Summary

After the React Router v6 is fully refactored with Hooks, it not only simplifies the code, but also becomes more useful. When the official version is released, it can be upgraded quickly.

In addition, from these optimizations made by React Router v6, we have discovered more ingenious use of Context from the source code. I hope this method can help you to apply it to other more complex project designs. Thank you for your reference- http://bjbsair.com/2020-04-01/tech-info/18408.html

Pictures from the Internet

1 Introduction

The version of react route V6 alpha has been released. This week, we will analyze the changes through the article a snake peak at react route V6.

React Router v6: https://github.com/ReactTraining/react-router

A Sneak Peek at React Router v6 : https://alligator.io/react/react-router-v6/

2 Overview

Renamed

A painless change makes API naming more standard.

// v5  
import { BrowserRouter, Switch, Route } from "react-router-dom";  
  
function App() {  
  return (  
    <BrowserRouter>  
      <Switch>  
        <Route exact path="/">  
          <Home />  
        </Route>  
        <Route path="/profile">  
          <Profile />  
        </Route>  
      </Switch>  
    </BrowserRouter>  
  );  
}

In the V6 version of react router, directly use Routes instead of Switch:

// v6  
import { BrowserRouter, Routes, Route } from "react-router-dom";  
  
function App() {  
  return (  
    <BrowserRouter>  
      <Routes>  
        <Route path="/" element={<Home />} />  
        <Route path="profile/*" element={<Profile />} />  
      </Routes>  
    </BrowserRouter>  
  );  
}

upgrade

In v5 version, it is not very intuitive to pass parameters to components. You need to use RenderProps to pass through routeProps:

import Profile from './Profile';  
  
// v5  
<Route path=":userId" component={Profile} />  
<Route  
  path=":userId"  
  render={routeProps => (  
    <Profile {...routeProps} animate={true} />  
  )}  
/>  
  
// v6  
<Route path=":userId" element={<Profile />} />  
<Route path=":userId" element={<Profile animate={true} />} />

In v6, the render and component schemes are combined into element schemes, which can easily pass props without passing through the roteProps parameters.

More convenient nested routing

In v5, nested routes need to get match through useRouteMatch, and sub routes can be realized through match.path splicing:

// v5  
import {  
  BrowserRouter,  
  Switch,  
  Route,  
  Link,  
  useRouteMatch  
} from "react-router-dom";  
  
function App() {  
  return (  
    <BrowserRouter>  
      <Switch>  
        <Route exact path="/" component={Home} />  
        <Route path="/profile" component={Profile} />  
      </Switch>  
    </BrowserRouter>  
  );  
}  
  
function Profile() {  
  let match = useRouteMatch();  
  
  return (  
    <div>  
      <nav>  
        <Link to={`${match.url}/me`}>My Profile</Link>  
      </nav>  
  
      <Switch>  
        <Route path={`${match.path}/me`}>  
          <MyProfile />  
        </Route>  
        <Route path={`${match.path}/:id`}>  
          <OthersProfile />  
        </Route>  
      </Switch>  
    </div>  
  );  
}

In the v6 version, the useRouteMatch step is omitted, and the relative path can be directly represented by path:

// v6  
import { BrowserRouter, Routes, Route, Link, Outlet } from "react-router-dom";  
  
// Approach #1  
function App() {  
  return (  
    <BrowserRouter>  
      <Routes>  
        <Route path="/" element={<Home />} />  
        <Route path="profile/*" element={<Profile />} />  
      </Routes>  
    </BrowserRouter>  
  );  
}  
  
function Profile() {  
  return (  
    <div>  
      <nav>  
        <Link to="me">My Profile</Link>  
      </nav>  
  
      <Routes>  
        <Route path="me" element={<MyProfile />} />  
        <Route path=":id" element={<OthersProfile />} />  
      </Routes>  
    </div>  
  );  
}  
  
// Approach #2  
// You can also define all  
// <Route> in a single place  
function App() {  
  return (  
    <BrowserRouter>  
      <Routes>  
        <Route path="/" element={<Home />} />  
        <Route path="profile" element={<Profile />}>  
          <Route path=":id" element={<MyProfile />} />  
          <Route path="me" element={<OthersProfile />} />  
        </Route>  
      </Routes>  
    </BrowserRouter>  
  );  
}  
  
function Profile() {  
  return (  
    <div>  
      <nav>  
        <Link to="me">My Profile</Link>  
      </nav>  
  
      <Outlet />  
    </div>  
  );  
}

Note that the Outlet is the Element of the render sub route.

useNavigate instead of useHistory

In v5, the active jump route can be used for history.push and other operations through useHistory

// v5  
import { useHistory } from "react-router-dom";  
  
function MyButton() {  
  let history = useHistory();  
  function handleClick() {  
    history.push("/home");  
  }  
  return <button onClick={handleClick}>Submit</button>;  
}

In v6, you can directly implement this common operation through useNavigate:

// v6  
import { useNavigate } from "react-router-dom";  
  
function MyButton() {  
  let navigate = useNavigate();  
  function handleClick() {  
    navigate("/home");  
  }  
  return <button onClick={handleClick}>Submit</button>;  
}

The history is encapsulated in the react router. If history.replace is needed, you can specify it with the {replace: true} parameter:

// v5  
history.push("/home");  
history.replace("/home");  
  
// v6  
navigate("/home");  
navigate("/home", { replace: true });

Smaller volume 8kb

Due to the almost reconstruction of the code, the volume of the v6 version is reduced from 20kb to 8kb after compression.

3 intensive reading

There is a core idea in the source code of react router V6. The author will share it with you, which is very helpful for some framework development. Let's take a look at the code excerpt of useRoutes:

export function useRoutes(routes, basename = "", caseSensitive = false) {  
  let {  
    params: parentParams,  
    pathname: parentPathname,  
    route: parentRoute  
  } = React.useContext(RouteContext);  
  
  if (warnAboutMissingTrailingSplatAt) {  
    // ...  
  }  
  
  basename = basename ? joinPaths([parentPathname, basename]) : parentPathname;  
  
  let navigate = useNavigate();  
  let location = useLocation();  
  let matches = React.useMemo(  
    () => matchRoutes(routes, location, basename, caseSensitive),  
    [routes, location, basename, caseSensitive]  
  );  
  
  // ...  
  
  // Otherwise render an element.  
  let element = matches.reduceRight((outlet, { params, pathname, route }) => {  
    return (  
      <RouteContext.Provider  
        children={route.element}  
        value={{  
          outlet,  
          params: readOnly({ ...parentParams, ...params }),  
          pathname: joinPaths([basename, pathname]),  
          route  
        }}  
      />  
    );  
  }, null);  
  
  return element;  
}

As you can see, with React.Context, the v6 version wraps a layer of RouteContext when rendering each routing element.

For more convenient routing nesting:

In v6 version, useRouteMatch is omitted, and path is directly used to represent relative path.

This is achieved by using this scheme. Because each layer of route file is wrapped with Context, the path of the previous layer can be obtained at each layer. Therefore, when splicing routes, it can be completely implemented by the framework, without the need for users to splice them in advance when invoking.

Taking useNavigate as an example, some people think that the encapsulation of navigation only stays in the formal layer, but it also has encapsulation in function. For example, if the incoming path is a relative path, it will switch according to the current route. Here is the excerpt of useNavigate Code:

export function useNavigate() {  
  let { history, pending } = React.useContext(LocationContext);  
  let { pathname } = React.useContext(RouteContext);  
  
  let navigate = React.useCallback(  
    (to, { replace, state } = {}) => {  
      if (typeof to === "number") {  
        history.go(to);  
      } else {  
        let relativeTo = resolveLocation(to, pathname);  
  
        let method = !!replace || pending ? "replace" : "push";  
        history[method](relativeTo, state);  
      }  
    },  
    [history, pending, pathname]  
  );  
  
  return navigate;  
}

You can see that RouteContext is used to get the current pathname, and to and pathname are spliced according to resolveLocation, and pathname is provided through RouteContext.Provider.

Using multi-layer Context Provider skillfully

Many times, we use Context to stay at the level of one Provider and multiple usecontexts, which is the most basic usage of Context. However, we believe that after reading React Router v6, we can find more usage of Context: multi-layer Context Provider.

Although it is said that there are multiple layers of Context Provider that will adopt the principle of recent coverage, this is not only a function to avoid errors, we can use this function to achieve such improvements as React Router v6.

To illustrate this feature in more detail, here is another specific example: for example, when building a rendering engine, each component has an id, but this id does not show up in the props of the component:

const Input = () => {  
  // The Input component will automatically generate an id in the canvas, but this id component cannot be obtained through props  
};

At this time, if we allow another sub element to be created inside the Input component and want the id of this sub element to be derived from Input, we may need the user to do this:

const Input = ({ id }) => {  
  return <ComponentLoader id={id + "1"} />;  
};

There are two problems in doing so:

  1. Exposing the id to the Input component violates the simplicity of the previous design.
  2. The component needs to assemble the id, which is very troublesome.

The problems encountered here are the same as those encountered by React Router. Can we simplify the code as follows, but the function remains the same?

const Input = () => {  
  return <ComponentLoader id="1" />;  
};

The answer is yes, we can use Context to implement this solution. The key point is to render Input, but the component container needs to wrap a Provider:

const ComponentLoader = ({ id, element }) => {  
  <Context.Provider value={{ id }}>{element}</Context.Provider>;  
};

For internal components, the id obtained by calling useContext at different levels is different, which is exactly the effect we want:

const ComponentLoader = ({id,element}) => {  
  const { id: parentId } = useContext(Context)  
  
  <Context.Provider value={{ id: parentId + id }}>  
    {element}  
  </Context.Provider>  
}

In this way, the < componentloader id = "1" / > we call inside Input actually splices the actual ID 01, which is completely thrown to the external engine layer for processing, and the user does not need to manually splice.

4 Summary

After the React Router v6 is fully refactored with Hooks, it not only simplifies the code, but also becomes more useful. When the official version is released, it can be upgraded quickly.

In addition, from these optimizations made by React Router v6, we have discovered more ingenious use of Context from the source code. I hope this method can help you to apply it to other more complex project designs. Thank you for your reference- http://bjbsair.com/2020-04-01/tech-info/18408.html

Pictures from the Internet

1 Introduction

The version of react route V6 alpha has been released. This week, we will analyze the changes through the article a snake peak at react route V6.

React Router v6: https://github.com/ReactTraining/react-router

A Sneak Peek at React Router v6 : https://alligator.io/react/react-router-v6/

2 Overview

Renamed

A painless change makes API naming more standard.

// v5  
import { BrowserRouter, Switch, Route } from "react-router-dom";  
  
function App() {  
  return (  
    <BrowserRouter>  
      <Switch>  
        <Route exact path="/">  
          <Home />  
        </Route>  
        <Route path="/profile">  
          <Profile />  
        </Route>  
      </Switch>  
    </BrowserRouter>  
  );  
}

In the V6 version of react router, directly use Routes instead of Switch:

// v6  
import { BrowserRouter, Routes, Route } from "react-router-dom";  
  
function App() {  
  return (  
    <BrowserRouter>  
      <Routes>  
        <Route path="/" element={<Home />} />  
        <Route path="profile/*" element={<Profile />} />  
      </Routes>  
    </BrowserRouter>  
  );  
}

upgrade

In v5 version, it is not very intuitive to pass parameters to components. You need to use RenderProps to pass through routeProps:

import Profile from './Profile';  
  
// v5  
<Route path=":userId" component={Profile} />  
<Route  
  path=":userId"  
  render={routeProps => (  
    <Profile {...routeProps} animate={true} />  
  )}  
/>  
  
// v6  
<Route path=":userId" element={<Profile />} />  
<Route path=":userId" element={<Profile animate={true} />} />

In v6, the render and component schemes are combined into element schemes, which can easily pass props without passing through the roteProps parameters.

More convenient nested routing

In v5, nested routes need to get match through useRouteMatch, and sub routes can be realized through match.path splicing:

// v5  
import {  
  BrowserRouter,  
  Switch,  
  Route,  
  Link,  
  useRouteMatch  
} from "react-router-dom";  
  
function App() {  
  return (  
    <BrowserRouter>  
      <Switch>  
        <Route exact path="/" component={Home} />  
        <Route path="/profile" component={Profile} />  
      </Switch>  
    </BrowserRouter>  
  );  
}  
  
function Profile() {  
  let match = useRouteMatch();  
  
  return (  
    <div>  
      <nav>  
        <Link to={`${match.url}/me`}>My Profile</Link>  
      </nav>  
  
      <Switch>  
        <Route path={`${match.path}/me`}>  
          <MyProfile />  
        </Route>  
        <Route path={`${match.path}/:id`}>  
          <OthersProfile />  
        </Route>  
      </Switch>  
    </div>  
  );  
}

In the v6 version, the useRouteMatch step is omitted, and the relative path can be directly represented by path:

// v6  
import { BrowserRouter, Routes, Route, Link, Outlet } from "react-router-dom";  
  
// Approach #1  
function App() {  
  return (  
    <BrowserRouter>  
      <Routes>  
        <Route path="/" element={<Home />} />  
        <Route path="profile/*" element={<Profile />} />  
      </Routes>  
    </BrowserRouter>  
  );  
}  
  
function Profile() {  
  return (  
    <div>  
      <nav>  
        <Link to="me">My Profile</Link>  
      </nav>  
  
      <Routes>  
        <Route path="me" element={<MyProfile />} />  
        <Route path=":id" element={<OthersProfile />} />  
      </Routes>  
    </div>  
  );  
}  
  
// Approach #2  
// You can also define all  
// <Route> in a single place  
function App() {  
  return (  
    <BrowserRouter>  
      <Routes>  
        <Route path="/" element={<Home />} />  
        <Route path="profile" element={<Profile />}>  
          <Route path=":id" element={<MyProfile />} />  
          <Route path="me" element={<OthersProfile />} />  
        </Route>  
      </Routes>  
    </BrowserRouter>  
  );  
}  
  
function Profile() {  
  return (  
    <div>  
      <nav>  
        <Link to="me">My Profile</Link>  
      </nav>  
  
      <Outlet />  
    </div>  
  );  
}

Note that the Outlet is the Element of the render sub route.

useNavigate instead of useHistory

In v5, the active jump route can be used for history.push and other operations through useHistory

// v5  
import { useHistory } from "react-router-dom";  
  
function MyButton() {  
  let history = useHistory();  
  function handleClick() {  
    history.push("/home");  
  }  
  return <button onClick={handleClick}>Submit</button>;  
}

In v6, you can directly implement this common operation through useNavigate:

// v6  
import { useNavigate } from "react-router-dom";  
  
function MyButton() {  
  let navigate = useNavigate();  
  function handleClick() {  
    navigate("/home");  
  }  
  return <button onClick={handleClick}>Submit</button>;  
}

The history is encapsulated in the react router. If history.replace is needed, you can specify it with the {replace: true} parameter:

// v5  
history.push("/home");  
history.replace("/home");  
  
// v6  
navigate("/home");  
navigate("/home", { replace: true });

Smaller volume 8kb

Due to the almost reconstruction of the code, the volume of the v6 version is reduced from 20kb to 8kb after compression.

3 intensive reading

There is a core idea in the source code of react router V6. The author will share it with you, which is very helpful for some framework development. Let's take a look at the code excerpt of useRoutes:

export function useRoutes(routes, basename = "", caseSensitive = false) {  
  let {  
    params: parentParams,  
    pathname: parentPathname,  
    route: parentRoute  
  } = React.useContext(RouteContext);  
  
  if (warnAboutMissingTrailingSplatAt) {  
    // ...  
  }  
  
  basename = basename ? joinPaths([parentPathname, basename]) : parentPathname;  
  
  let navigate = useNavigate();  
  let location = useLocation();  
  let matches = React.useMemo(  
    () => matchRoutes(routes, location, basename, caseSensitive),  
    [routes, location, basename, caseSensitive]  
  );  
  
  // ...  
  
  // Otherwise render an element.  
  let element = matches.reduceRight((outlet, { params, pathname, route }) => {  
    return (  
      <RouteContext.Provider  
        children={route.element}  
        value={{  
          outlet,  
          params: readOnly({ ...parentParams, ...params }),  
          pathname: joinPaths([basename, pathname]),  
          route  
        }}  
      />  
    );  
  }, null);  
  
  return element;  
}

As you can see, with React.Context, the v6 version wraps a layer of RouteContext when rendering each routing element.

For more convenient routing nesting:

In v6 version, useRouteMatch is omitted, and path is directly used to represent relative path.

This is achieved by using this scheme. Because each layer of route file is wrapped with Context, the path of the previous layer can be obtained at each layer. Therefore, when splicing routes, it can be completely implemented by the framework, without the need for users to splice them in advance when invoking.

Taking useNavigate as an example, some people think that the encapsulation of navigation only stays in the formal layer, but it also has encapsulation in function. For example, if the incoming path is a relative path, it will switch according to the current route. Here is the excerpt of useNavigate Code:

export function useNavigate() {  
  let { history, pending } = React.useContext(LocationContext);  
  let { pathname } = React.useContext(RouteContext);  
  
  let navigate = React.useCallback(  
    (to, { replace, state } = {}) => {  
      if (typeof to === "number") {  
        history.go(to);  
      } else {  
        let relativeTo = resolveLocation(to, pathname);  
  
        let method = !!replace || pending ? "replace" : "push";  
        history[method](relativeTo, state);  
      }  
    },  
    [history, pending, pathname]  
  );  
  
  return navigate;  
}

You can see that RouteContext is used to get the current pathname, and to and pathname are spliced according to resolveLocation, and pathname is provided through RouteContext.Provider.

Using multi-layer Context Provider skillfully

Many times, we use Context to stay at the level of one Provider and multiple usecontexts, which is the most basic usage of Context. However, we believe that after reading React Router v6, we can find more usage of Context: multi-layer Context Provider.

Although it is said that there are multiple layers of Context Provider that will adopt the principle of recent coverage, this is not only a function to avoid errors, we can use this function to achieve such improvements as React Router v6.

To illustrate this feature in more detail, here is another specific example: for example, when building a rendering engine, each component has an id, but this id does not show up in the props of the component:

const Input = () => {  
  // The Input component will automatically generate an id in the canvas, but this id component cannot be obtained through props  
};

At this time, if we allow another sub element to be created inside the Input component and want the id of this sub element to be derived from Input, we may need the user to do this:

const Input = ({ id }) => {  
  return <ComponentLoader id={id + "1"} />;  
};

There are two problems in doing so:

  1. Exposing the id to the Input component violates the simplicity of the previous design.
  2. The component needs to assemble the id, which is very troublesome.

The problems encountered here are the same as those encountered by React Router. Can we simplify the code as follows, but the function remains the same?

const Input = () => {  
  return <ComponentLoader id="1" />;  
};

The answer is yes, we can use Context to implement this solution. The key point is to render Input, but the component container needs to wrap a Provider:

const ComponentLoader = ({ id, element }) => {  
  <Context.Provider value={{ id }}>{element}</Context.Provider>;  
};

For internal components, the id obtained by calling useContext at different levels is different, which is exactly the effect we want:

const ComponentLoader = ({id,element}) => {  
  const { id: parentId } = useContext(Context)  
  
  <Context.Provider value={{ id: parentId + id }}>  
    {element}  
  </Context.Provider>  
}

In this way, the < componentloader id = "1" / > we call inside Input actually splices the actual ID 01, which is completely thrown to the external engine layer for processing, and the user does not need to manually splice.

4 Summary

After the React Router v6 is fully refactored with Hooks, it not only simplifies the code, but also becomes more useful. When the official version is released, it can be upgraded quickly.

In addition, from these optimizations made by React Router v6, we have discovered more ingenious use of Context from the source code. I hope this method can help you to apply it to other more complex project designs. Thank you for your reference- http://bjbsair.com/2020-04-01/tech-info/18408.html

Pictures from the Internet

1 Introduction

The version of react route V6 alpha has been released. This week, we will analyze the changes through the article a snake peak at react route V6.

React Router v6: https://github.com/ReactTraining/react-router

A Sneak Peek at React Router v6 : https://alligator.io/react/react-router-v6/

2 Overview

Renamed

A painless change makes API naming more standard.

// v5  
import { BrowserRouter, Switch, Route } from "react-router-dom";  
  
function App() {  
  return (  
    <BrowserRouter>  
      <Switch>  
        <Route exact path="/">  
          <Home />  
        </Route>  
        <Route path="/profile">  
          <Profile />  
        </Route>  
      </Switch>  
    </BrowserRouter>  
  );  
}

In the V6 version of react router, directly use Routes instead of Switch:

// v6  
import { BrowserRouter, Routes, Route } from "react-router-dom";  
  
function App() {  
  return (  
    <BrowserRouter>  
      <Routes>  
        <Route path="/" element={<Home />} />  
        <Route path="profile/*" element={<Profile />} />  
      </Routes>  
    </BrowserRouter>  
  );  
}

upgrade

In v5 version, it is not very intuitive to pass parameters to components. You need to use RenderProps to pass through routeProps:

import Profile from './Profile';  
  
// v5  
<Route path=":userId" component={Profile} />  
<Route  
  path=":userId"  
  render={routeProps => (  
    <Profile {...routeProps} animate={true} />  
  )}  
/>  
  
// v6  
<Route path=":userId" element={<Profile />} />  
<Route path=":userId" element={<Profile animate={true} />} />

In v6, the render and component schemes are combined into element schemes, which can easily pass props without passing through the roteProps parameters.

More convenient nested routing

In v5, nested routes need to get match through useRouteMatch, and sub routes can be realized through match.path splicing:

// v5  
import {  
  BrowserRouter,  
  Switch,  
  Route,  
  Link,  
  useRouteMatch  
} from "react-router-dom";  
  
function App() {  
  return (  
    <BrowserRouter>  
      <Switch>  
        <Route exact path="/" component={Home} />  
        <Route path="/profile" component={Profile} />  
      </Switch>  
    </BrowserRouter>  
  );  
}  
  
function Profile() {  
  let match = useRouteMatch();  
  
  return (  
    <div>  
      <nav>  
        <Link to={`${match.url}/me`}>My Profile</Link>  
      </nav>  
  
      <Switch>  
        <Route path={`${match.path}/me`}>  
          <MyProfile />  
        </Route>  
        <Route path={`${match.path}/:id`}>  
          <OthersProfile />  
        </Route>  
      </Switch>  
    </div>  
  );  
}

In the v6 version, the useRouteMatch step is omitted, and the relative path can be directly represented by path:

// v6  
import { BrowserRouter, Routes, Route, Link, Outlet } from "react-router-dom";  
  
// Approach #1  
function App() {  
  return (  
    <BrowserRouter>  
      <Routes>  
        <Route path="/" element={<Home />} />  
        <Route path="profile/*" element={<Profile />} />  
      </Routes>  
    </BrowserRouter>  
  );  
}  
  
function Profile() {  
  return (  
    <div>  
      <nav>  
        <Link to="me">My Profile</Link>  
      </nav>  
  
      <Routes>  
        <Route path="me" element={<MyProfile />} />  
        <Route path=":id" element={<OthersProfile />} />  
      </Routes>  
    </div>  
  );  
}  
  
// Approach #2  
// You can also define all  
// <Route> in a single place  
function App() {  
  return (  
    <BrowserRouter>  
      <Routes>  
        <Route path="/" element={<Home />} />  
        <Route path="profile" element={<Profile />}>  
          <Route path=":id" element={<MyProfile />} />  
          <Route path="me" element={<OthersProfile />} />  
        </Route>  
      </Routes>  
    </BrowserRouter>  
  );  
}  
  
function Profile() {  
  return (  
    <div>  
      <nav>  
        <Link to="me">My Profile</Link>  
      </nav>  
  
      <Outlet />  
    </div>  
  );  
}

Note that the Outlet is the Element of the render sub route.

useNavigate instead of useHistory

In v5, the active jump route can be used for history.push and other operations through useHistory

// v5  
import { useHistory } from "react-router-dom";  
  
function MyButton() {  
  let history = useHistory();  
  function handleClick() {  
    history.push("/home");  
  }  
  return <button onClick={handleClick}>Submit</button>;  
}

In v6, you can directly implement this common operation through useNavigate:

// v6  
import { useNavigate } from "react-router-dom";  
  
function MyButton() {  
  let navigate = useNavigate();  
  function handleClick() {  
    navigate("/home");  
  }  
  return <button onClick={handleClick}>Submit</button>;  
}

The history is encapsulated in the react router. If history.replace is needed, you can specify it with the {replace: true} parameter:

// v5  
history.push("/home");  
history.replace("/home");  
  
// v6  
navigate("/home");  
navigate("/home", { replace: true });

Smaller volume 8kb

Due to the almost reconstruction of the code, the volume of the v6 version is reduced from 20kb to 8kb after compression.

3 intensive reading

There is a core idea in the source code of react router V6. The author will share it with you, which is very helpful for some framework development. Let's take a look at the code excerpt of useRoutes:

export function useRoutes(routes, basename = "", caseSensitive = false) {  
  let {  
    params: parentParams,  
    pathname: parentPathname,  
    route: parentRoute  
  } = React.useContext(RouteContext);  
  
  if (warnAboutMissingTrailingSplatAt) {  
    // ...  
  }  
  
  basename = basename ? joinPaths([parentPathname, basename]) : parentPathname;  
  
  let navigate = useNavigate();  
  let location = useLocation();  
  let matches = React.useMemo(  
    () => matchRoutes(routes, location, basename, caseSensitive),  
    [routes, location, basename, caseSensitive]  
  );  
  
  // ...  
  
  // Otherwise render an element.  
  let element = matches.reduceRight((outlet, { params, pathname, route }) => {  
    return (  
      <RouteContext.Provider  
        children={route.element}  
        value={{  
          outlet,  
          params: readOnly({ ...parentParams, ...params }),  
          pathname: joinPaths([basename, pathname]),  
          route  
        }}  
      />  
    );  
  }, null);  
  
  return element;  
}

As you can see, with React.Context, the v6 version wraps a layer of RouteContext when rendering each routing element.

For more convenient routing nesting:

In v6 version, useRouteMatch is omitted, and path is directly used to represent relative path.

This is achieved by using this scheme. Because each layer of route file is wrapped with Context, the path of the previous layer can be obtained at each layer. Therefore, when splicing routes, it can be completely implemented by the framework, without the need for users to splice them in advance when invoking.

Taking useNavigate as an example, some people think that the encapsulation of navigation only stays in the formal layer, but it also has encapsulation in function. For example, if the incoming path is a relative path, it will switch according to the current route. Here is the excerpt of useNavigate Code:

export function useNavigate() {  
  let { history, pending } = React.useContext(LocationContext);  
  let { pathname } = React.useContext(RouteContext);  
  
  let navigate = React.useCallback(  
    (to, { replace, state } = {}) => {  
      if (typeof to === "number") {  
        history.go(to);  
      } else {  
        let relativeTo = resolveLocation(to, pathname);  
  
        let method = !!replace || pending ? "replace" : "push";  
        history[method](relativeTo, state);  
      }  
    },  
    [history, pending, pathname]  
  );  
  
  return navigate;  
}

You can see that RouteContext is used to get the current pathname, and to and pathname are spliced according to resolveLocation, and pathname is provided through RouteContext.Provider.

Using multi-layer Context Provider skillfully

Many times, we use Context to stay at the level of one Provider and multiple usecontexts, which is the most basic usage of Context. However, we believe that after reading React Router v6, we can find more usage of Context: multi-layer Context Provider.

Although it is said that there are multiple layers of Context Provider that will adopt the principle of recent coverage, this is not only a function to avoid errors, we can use this function to achieve such improvements as React Router v6.

To illustrate this feature in more detail, here is another specific example: for example, when building a rendering engine, each component has an id, but this id does not show up in the props of the component:

const Input = () => {  
  // The Input component will automatically generate an id in the canvas, but this id component cannot be obtained through props  
};

At this time, if we allow another sub element to be created inside the Input component and want the id of this sub element to be derived from Input, we may need the user to do this:

const Input = ({ id }) => {  
  return <ComponentLoader id={id + "1"} />;  
};

There are two problems in doing so:

  1. Exposing the id to the Input component violates the simplicity of the previous design.
  2. The component needs to assemble the id, which is very troublesome.

The problems encountered here are the same as those encountered by React Router. Can we simplify the code as follows, but the function remains the same?

const Input = () => {  
  return <ComponentLoader id="1" />;  
};

The answer is yes, we can use Context to implement this solution. The key point is to render Input, but the component container needs to wrap a Provider:

const ComponentLoader = ({ id, element }) => {  
  <Context.Provider value={{ id }}>{element}</Context.Provider>;  
};

For internal components, the id obtained by calling useContext at different levels is different, which is exactly the effect we want:

const ComponentLoader = ({id,element}) => {  
  const { id: parentId } = useContext(Context)  
  
  <Context.Provider value={{ id: parentId + id }}>  
    {element}  
  </Context.Provider>  
}

In this way, the < componentloader id = "1" / > we call inside Input actually splices the actual ID 01, which is completely thrown to the external engine layer for processing, and the user does not need to manually splice.

4 Summary

After the React Router v6 is fully refactored with Hooks, it not only simplifies the code, but also becomes more useful. When the official version is released, it can be upgraded quickly.

In addition, from these optimizations made by React Router v6, we have discovered more ingenious use of Context from the source code. I hope this method can help you to apply it to other more complex project designs. Thank you for your reference- http://bjbsair.com/2020-04-01/tech-info/18408.html

Pictures from the Internet

1 Introduction

The version of react route V6 alpha has been released. This week, we will analyze the changes through the article a snake peak at react route V6.

React Router v6: https://github.com/ReactTraining/react-router

A Sneak Peek at React Router v6 : https://alligator.io/react/react-router-v6/

2 Overview

Renamed

A painless change makes API naming more standard.

// v5  
import { BrowserRouter, Switch, Route } from "react-router-dom";  
  
function App() {  
  return (  
    <BrowserRouter>  
      <Switch>  
        <Route exact path="/">  
          <Home />  
        </Route>  
        <Route path="/profile">  
          <Profile />  
        </Route>  
      </Switch>  
    </BrowserRouter>  
  );  
}

In the V6 version of react router, directly use Routes instead of Switch:

// v6  
import { BrowserRouter, Routes, Route } from "react-router-dom";  
  
function App() {  
  return (  
    <BrowserRouter>  
      <Routes>  
        <Route path="/" element={<Home />} />  
        <Route path="profile/*" element={<Profile />} />  
      </Routes>  
    </BrowserRouter>  
  );  
}

upgrade

In v5 version, it is not very intuitive to pass parameters to components. You need to use RenderProps to pass through routeProps:

import Profile from './Profile';  
  
// v5  
<Route path=":userId" component={Profile} />  
<Route  
  path=":userId"  
  render={routeProps => (  
    <Profile {...routeProps} animate={true} />  
  )}  
/>  
  
// v6  
<Route path=":userId" element={<Profile />} />  
<Route path=":userId" element={<Profile animate={true} />} />

In v6, the render and component schemes are combined into element schemes, which can easily pass props without passing through the roteProps parameters.

More convenient nested routing

In v5, nested routes need to get match through useRouteMatch, and sub routes can be realized through match.path splicing:

// v5  
import {  
  BrowserRouter,  
  Switch,  
  Route,  
  Link,  
  useRouteMatch  
} from "react-router-dom";  
  
function App() {  
  return (  
    <BrowserRouter>  
      <Switch>  
        <Route exact path="/" component={Home} />  
        <Route path="/profile" component={Profile} />  
      </Switch>  
    </BrowserRouter>  
  );  
}  
  
function Profile() {  
  let match = useRouteMatch();  
  
  return (  
    <div>  
      <nav>  
        <Link to={`${match.url}/me`}>My Profile</Link>  
      </nav>  
  
      <Switch>  
        <Route path={`${match.path}/me`}>  
          <MyProfile />  
        </Route>  
        <Route path={`${match.path}/:id`}>  
          <OthersProfile />  
        </Route>  
      </Switch>  
    </div>  
  );  
}

In the v6 version, the useRouteMatch step is omitted, and the relative path can be directly represented by path:

// v6  
import { BrowserRouter, Routes, Route, Link, Outlet } from "react-router-dom";  
  
// Approach #1  
function App() {  
  return (  
    <BrowserRouter>  
      <Routes>  
        <Route path="/" element={<Home />} />  
        <Route path="profile/*" element={<Profile />} />  
      </Routes>  
    </BrowserRouter>  
  );  
}  
  
function Profile() {  
  return (  
    <div>  
      <nav>  
        <Link to="me">My Profile</Link>  
      </nav>  
  
      <Routes>  
        <Route path="me" element={<MyProfile />} />  
        <Route path=":id" element={<OthersProfile />} />  
      </Routes>  
    </div>  
  );  
}  
  
// Approach #2  
// You can also define all  
// <Route> in a single place  
function App() {  
  return (  
    <BrowserRouter>  
      <Routes>  
        <Route path="/" element={<Home />} />  
        <Route path="profile" element={<Profile />}>  
          <Route path=":id" element={<MyProfile />} />  
          <Route path="me" element={<OthersProfile />} />  
        </Route>  
      </Routes>  
    </BrowserRouter>  
  );  
}  
  
function Profile() {  
  return (  
    <div>  
      <nav>  
        <Link to="me">My Profile</Link>  
      </nav>  
  
      <Outlet />  
    </div>  
  );  
}

Note that the Outlet is the Element of the render sub route.

useNavigate instead of useHistory

In v5, the active jump route can be used for history.push and other operations through useHistory

// v5  
import { useHistory } from "react-router-dom";  
  
function MyButton() {  
  let history = useHistory();  
  function handleClick() {  
    history.push("/home");  
  }  
  return <button onClick={handleClick}>Submit</button>;  
}

In v6, you can directly implement this common operation through useNavigate:

// v6  
import { useNavigate } from "react-router-dom";  
  
function MyButton() {  
  let navigate = useNavigate();  
  function handleClick() {  
    navigate("/home");  
  }  
  return <button onClick={handleClick}>Submit</button>;  
}

The history is encapsulated in the react router. If history.replace is needed, you can specify it with the {replace: true} parameter:

// v5  
history.push("/home");  
history.replace("/home");  
  
// v6  
navigate("/home");  
navigate("/home", { replace: true });

Smaller volume 8kb

Due to the almost reconstruction of the code, the volume of the v6 version is reduced from 20kb to 8kb after compression.

3 intensive reading

There is a core idea in the source code of react router V6. The author will share it with you, which is very helpful for some framework development. Let's take a look at the code excerpt of useRoutes:

export function useRoutes(routes, basename = "", caseSensitive = false) {  
  let {  
    params: parentParams,  
    pathname: parentPathname,  
    route: parentRoute  
  } = React.useContext(RouteContext);  
  
  if (warnAboutMissingTrailingSplatAt) {  
    // ...  
  }  
  
  basename = basename ? joinPaths([parentPathname, basename]) : parentPathname;  
  
  let navigate = useNavigate();  
  let location = useLocation();  
  let matches = React.useMemo(  
    () => matchRoutes(routes, location, basename, caseSensitive),  
    [routes, location, basename, caseSensitive]  
  );  
  
  // ...  
  
  // Otherwise render an element.  
  let element = matches.reduceRight((outlet, { params, pathname, route }) => {  
    return (  
      <RouteContext.Provider  
        children={route.element}  
        value={{  
          outlet,  
          params: readOnly({ ...parentParams, ...params }),  
          pathname: joinPaths([basename, pathname]),  
          route  
        }}  
      />  
    );  
  }, null);  
  
  return element;  
}

As you can see, with React.Context, the v6 version wraps a layer of RouteContext when rendering each routing element.

For more convenient routing nesting:

In v6 version, useRouteMatch is omitted, and path is directly used to represent relative path.

This is achieved by using this scheme. Because each layer of route file is wrapped with Context, the path of the previous layer can be obtained at each layer. Therefore, when splicing routes, it can be completely implemented by the framework, without the need for users to splice them in advance when invoking.

Taking useNavigate as an example, some people think that the encapsulation of navigation only stays in the formal layer, but it also has encapsulation in function. For example, if the incoming path is a relative path, it will switch according to the current route. Here is the excerpt of useNavigate Code:

export function useNavigate() {  
  let { history, pending } = React.useContext(LocationContext);  
  let { pathname } = React.useContext(RouteContext);  
  
  let navigate = React.useCallback(  
    (to, { replace, state } = {}) => {  
      if (typeof to === "number") {  
        history.go(to);  
      } else {  
        let relativeTo = resolveLocation(to, pathname);  
  
        let method = !!replace || pending ? "replace" : "push";  
        history[method](relativeTo, state);  
      }  
    },  
    [history, pending, pathname]  
  );  
  
  return navigate;  
}

You can see that RouteContext is used to get the current pathname, and to and pathname are spliced according to resolveLocation, and pathname is provided through RouteContext.Provider.

Using multi-layer Context Provider skillfully

Many times, we use Context to stay at the level of one Provider and multiple usecontexts, which is the most basic usage of Context. However, we believe that after reading React Router v6, we can find more usage of Context: multi-layer Context Provider.

Although it is said that there are multiple layers of Context Provider that will adopt the principle of recent coverage, this is not only a function to avoid errors, we can use this function to achieve such improvements as React Router v6.

To illustrate this feature in more detail, here is another specific example: for example, when building a rendering engine, each component has an id, but this id does not show up in the props of the component:

const Input = () => {  
  // The Input component will automatically generate an id in the canvas, but this id component cannot be obtained through props  
};

At this time, if we allow another sub element to be created inside the Input component and want the id of this sub element to be derived from Input, we may need the user to do this:

const Input = ({ id }) => {  
  return <ComponentLoader id={id + "1"} />;  
};

There are two problems in doing so:

  1. Exposing the id to the Input component violates the simplicity of the previous design.
  2. The component needs to assemble the id, which is very troublesome.

The problems encountered here are the same as those encountered by React Router. Can we simplify the code as follows, but the function remains the same?

const Input = () => {  
  return <ComponentLoader id="1" />;  
};

The answer is yes, we can use Context to implement this solution. The key point is to render Input, but the component container needs to wrap a Provider:

const ComponentLoader = ({ id, element }) => {  
  <Context.Provider value={{ id }}>{element}</Context.Provider>;  
};

For internal components, the id obtained by calling useContext at different levels is different, which is exactly the effect we want:

const ComponentLoader = ({id,element}) => {  
  const { id: parentId } = useContext(Context)  
  
  <Context.Provider value={{ id: parentId + id }}>  
    {element}  
  </Context.Provider>  
}

In this way, the < componentloader id = "1" / > we call inside Input actually splices the actual ID 01, which is completely thrown to the external engine layer for processing, and the user does not need to manually splice.

4 Summary

After the React Router v6 is fully refactored with Hooks, it not only simplifies the code, but also becomes more useful. When the official version is released, it can be upgraded quickly.

In addition, from these optimizations made by React Router v6, we have discovered more ingenious use of Context from the source code. I hope this method can help you to apply it to other more complex project designs. Thank you for your reference- http://bjbsair.com/2020-04-01/tech-info/18408.html

Pictures from the Internet

1 Introduction

The version of react route V6 alpha has been released. This week, we will analyze the changes through the article a snake peak at react route V6.

React Router v6: https://github.com/ReactTraining/react-router

A Sneak Peek at React Router v6 : https://alligator.io/react/react-router-v6/

2 Overview

Renamed

A painless change makes API naming more standard.

// v5  
import { BrowserRouter, Switch, Route } from "react-router-dom";  
  
function App() {  
  return (  
    <BrowserRouter>  
      <Switch>  
        <Route exact path="/">  
          <Home />  
        </Route>  
        <Route path="/profile">  
          <Profile />  
        </Route>  
      </Switch>  
    </BrowserRouter>  
  );  
}

In the V6 version of react router, directly use Routes instead of Switch:

// v6  
import { BrowserRouter, Routes, Route } from "react-router-dom";  
  
function App() {  
  return (  
    <BrowserRouter>  
      <Routes>  
        <Route path="/" element={<Home />} />  
        <Route path="profile/*" element={<Profile />} />  
      </Routes>  
    </BrowserRouter>  
  );  
}

upgrade

In v5 version, it is not very intuitive to pass parameters to components. You need to use RenderProps to pass through routeProps:

import Profile from './Profile';  
  
// v5  
<Route path=":userId" component={Profile} />  
<Route  
  path=":userId"  
  render={routeProps => (  
    <Profile {...routeProps} animate={true} />  
  )}  
/>  
  
// v6  
<Route path=":userId" element={<Profile />} />  
<Route path=":userId" element={<Profile animate={true} />} />

In v6, the render and component schemes are combined into element schemes, which can easily pass props without passing through the roteProps parameters.

More convenient nested routing

In v5, nested routes need to get match through useRouteMatch, and sub routes can be realized through match.path splicing:

// v5  
import {  
  BrowserRouter,  
  Switch,  
  Route,  
  Link,  
  useRouteMatch  
} from "react-router-dom";  
  
function App() {  
  return (  
    <BrowserRouter>  
      <Switch>  
        <Route exact path="/" component={Home} />  
        <Route path="/profile" component={Profile} />  
      </Switch>  
    </BrowserRouter>  
  );  
}  
  
function Profile() {  
  let match = useRouteMatch();  
  
  return (  
    <div>  
      <nav>  
        <Link to={`${match.url}/me`}>My Profile</Link>  
      </nav>  
  
      <Switch>  
        <Route path={`${match.path}/me`}>  
          <MyProfile />  
        </Route>  
        <Route path={`${match.path}/:id`}>  
          <OthersProfile />  
        </Route>  
      </Switch>  
    </div>  
  );  
}

In the v6 version, the useRouteMatch step is omitted, and the relative path can be directly represented by path:

// v6  
import { BrowserRouter, Routes, Route, Link, Outlet } from "react-router-dom";  
  
// Approach #1  
function App() {  
  return (  
    <BrowserRouter>  
      <Routes>  
        <Route path="/" element={<Home />} />  
        <Route path="profile/*" element={<Profile />} />  
      </Routes>  
    </BrowserRouter>  
  );  
}  
  
function Profile() {  
  return (  
    <div>  
      <nav>  
        <Link to="me">My Profile</Link>  
      </nav>  
  
      <Routes>  
        <Route path="me" element={<MyProfile />} />  
        <Route path=":id" element={<OthersProfile />} />  
      </Routes>  
    </div>  
  );  
}  
  
// Approach #2  
// You can also define all  
// <Route> in a single place  
function App() {  
  return (  
    <BrowserRouter>  
      <Routes>  
        <Route path="/" element={<Home />} />  
        <Route path="profile" element={<Profile />}>  
          <Route path=":id" element={<MyProfile />} />  
          <Route path="me" element={<OthersProfile />} />  
        </Route>  
      </Routes>  
    </BrowserRouter>  
  );  
}  
  
function Profile() {  
  return (  
    <div>  
      <nav>  
        <Link to="me">My Profile</Link>  
      </nav>  
  
      <Outlet />  
    </div>  
  );  
}

Note that the Outlet is the Element of the render sub route.

useNavigate instead of useHistory

In v5, the active jump route can be used for history.push and other operations through useHistory

// v5  
import { useHistory } from "react-router-dom";  
  
function MyButton() {  
  let history = useHistory();  
  function handleClick() {  
    history.push("/home");  
  }  
  return <button onClick={handleClick}>Submit</button>;  
}

In v6, you can directly implement this common operation through useNavigate:

// v6  
import { useNavigate } from "react-router-dom";  
  
function MyButton() {  
  let navigate = useNavigate();  
  function handleClick() {  
    navigate("/home");  
  }  
  return <button onClick={handleClick}>Submit</button>;  
}

The history is encapsulated in the react router. If history.replace is needed, you can specify it with the {replace: true} parameter:

// v5  
history.push("/home");  
history.replace("/home");  
  
// v6  
navigate("/home");  
navigate("/home", { replace: true });

Smaller volume 8kb

Due to the almost reconstruction of the code, the volume of the v6 version is reduced from 20kb to 8kb after compression.

3 intensive reading

There is a core idea in the source code of react router V6. The author will share it with you, which is very helpful for some framework development. Let's take a look at the code excerpt of useRoutes:

export function useRoutes(routes, basename = "", caseSensitive = false) {  
  let {  
    params: parentParams,  
    pathname: parentPathname,  
    route: parentRoute  
  } = React.useContext(RouteContext);  
  
  if (warnAboutMissingTrailingSplatAt) {  
    // ...  
  }  
  
  basename = basename ? joinPaths([parentPathname, basename]) : parentPathname;  
  
  let navigate = useNavigate();  
  let location = useLocation();  
  let matches = React.useMemo(  
    () => matchRoutes(routes, location, basename, caseSensitive),  
    [routes, location, basename, caseSensitive]  
  );  
  
  // ...  
  
  // Otherwise render an element.  
  let element = matches.reduceRight((outlet, { params, pathname, route }) => {  
    return (  
      <RouteContext.Provider  
        children={route.element}  
        value={{  
          outlet,  
          params: readOnly({ ...parentParams, ...params }),  
          pathname: joinPaths([basename, pathname]),  
          route  
        }}  
      />  
    );  
  }, null);  
  
  return element;  
}

As you can see, with React.Context, the v6 version wraps a layer of RouteContext when rendering each routing element.

For more convenient routing nesting:

In v6 version, useRouteMatch is omitted, and path is directly used to represent relative path.

This is achieved by using this scheme. Because each layer of route file is wrapped with Context, the path of the previous layer can be obtained at each layer. Therefore, when splicing routes, it can be completely implemented by the framework, without the need for users to splice them in advance when invoking.

Taking useNavigate as an example, some people think that the encapsulation of navigation only stays in the formal layer, but it also has encapsulation in function. For example, if the incoming path is a relative path, it will switch according to the current route. Here is the excerpt of useNavigate Code:

export function useNavigate() {  
  let { history, pending } = React.useContext(LocationContext);  
  let { pathname } = React.useContext(RouteContext);  
  
  let navigate = React.useCallback(  
    (to, { replace, state } = {}) => {  
      if (typeof to === "number") {  
        history.go(to);  
      } else {  
        let relativeTo = resolveLocation(to, pathname);  
  
        let method = !!replace || pending ? "replace" : "push";  
        history[method](relativeTo, state);  
      }  
    },  
    [history, pending, pathname]  
  );  
  
  return navigate;  
}

You can see that RouteContext is used to get the current pathname, and to and pathname are spliced according to resolveLocation, and pathname is provided through RouteContext.Provider.

Using multi-layer Context Provider skillfully

Many times, we use Context to stay at the level of one Provider and multiple usecontexts, which is the most basic usage of Context. However, we believe that after reading React Router v6, we can find more usage of Context: multi-layer Context Provider.

Although it is said that there are multiple layers of Context Provider that will adopt the principle of recent coverage, this is not only a function to avoid errors, we can use this function to achieve such improvements as React Router v6.

To illustrate this feature in more detail, here is another specific example: for example, when building a rendering engine, each component has an id, but this id does not show up in the props of the component:

const Input = () => {  
  // The Input component will automatically generate an id in the canvas, but this id component cannot be obtained through props  
};

At this time, if we allow another sub element to be created inside the Input component and want the id of this sub element to be derived from Input, we may need the user to do this:

const Input = ({ id }) => {  
  return <ComponentLoader id={id + "1"} />;  
};

There are two problems in doing so:

  1. Exposing the id to the Input component violates the simplicity of the previous design.
  2. The component needs to assemble the id, which is very troublesome.

The problems encountered here are the same as those encountered by React Router. Can we simplify the code as follows, but the function remains the same?

const Input = () => {  
  return <ComponentLoader id="1" />;  
};

The answer is yes, we can use Context to implement this solution. The key point is to render Input, but the component container needs to wrap a Provider:

const ComponentLoader = ({ id, element }) => {  
  <Context.Provider value={{ id }}>{element}</Context.Provider>;  
};

For internal components, the id obtained by calling useContext at different levels is different, which is exactly the effect we want:

const ComponentLoader = ({id,element}) => {  
  const { id: parentId } = useContext(Context)  
  
  <Context.Provider value={{ id: parentId + id }}>  
    {element}  
  </Context.Provider>  
}

In this way, the < componentloader id = "1" / > we call inside Input actually splices the actual ID 01, which is completely thrown to the external engine layer for processing, and the user does not need to manually splice.

4 Summary

After the React Router v6 is fully refactored with Hooks, it not only simplifies the code, but also becomes more useful. When the official version is released, it can be upgraded quickly.

In addition, from these optimizations made by React Router v6, we have discovered more ingenious use of Context from the source code. I hope this method can help you to apply it to other more complex project designs. Thank you for your reference- http://bjbsair.com/2020-04-01/tech-info/18408.html

Pictures from the Internet

1 Introduction

The version of react route V6 alpha has been released. This week, we will analyze the changes through the article a snake peak at react route V6.

React Router v6: https://github.com/ReactTraining/react-router

A Sneak Peek at React Router v6 : https://alligator.io/react/react-router-v6/

2 Overview

Renamed

A painless change makes API naming more standard.

// v5  
import { BrowserRouter, Switch, Route } from "react-router-dom";  
  
function App() {  
  return (  
    <BrowserRouter>  
      <Switch>  
        <Route exact path="/">  
          <Home />  
        </Route>  
        <Route path="/profile">  
          <Profile />  
        </Route>  
      </Switch>  
    </BrowserRouter>  
  );  
}

In the V6 version of react router, directly use Routes instead of Switch:

// v6  
import { BrowserRouter, Routes, Route } from "react-router-dom";  
  
function App() {  
  return (  
    <BrowserRouter>  
      <Routes>  
        <Route path="/" element={<Home />} />  
        <Route path="profile/*" element={<Profile />} />  
      </Routes>  
    </BrowserRouter>  
  );  
}

upgrade

In v5 version, it is not very intuitive to pass parameters to components. You need to use RenderProps to pass through routeProps:

import Profile from './Profile';  
  
// v5  
<Route path=":userId" component={Profile} />  
<Route  
  path=":userId"  
  render={routeProps => (  
    <Profile {...routeProps} animate={true} />  
  )}  
/>  
  
// v6  
<Route path=":userId" element={<Profile />} />  
<Route path=":userId" element={<Profile animate={true} />} />

In v6, the render and component schemes are combined into element schemes, which can easily pass props without passing through the roteProps parameters.

More convenient nested routing

In v5, nested routes need to get match through useRouteMatch, and sub routes can be realized through match.path splicing:

// v5  
import {  
  BrowserRouter,  
  Switch,  
  Route,  
  Link,  
  useRouteMatch  
} from "react-router-dom";  
  
function App() {  
  return (  
    <BrowserRouter>  
      <Switch>  
        <Route exact path="/" component={Home} />  
        <Route path="/profile" component={Profile} />  
      </Switch>  
    </BrowserRouter>  
  );  
}  
  
function Profile() {  
  let match = useRouteMatch();  
  
  return (  
    <div>  
      <nav>  
        <Link to={`${match.url}/me`}>My Profile</Link>  
      </nav>  
  
      <Switch>  
        <Route path={`${match.path}/me`}>  
          <MyProfile />  
        </Route>  
        <Route path={`${match.path}/:id`}>  
          <OthersProfile />  
        </Route>  
      </Switch>  
    </div>  
  );  
}

In the v6 version, the useRouteMatch step is omitted, and the relative path can be directly represented by path:

// v6  
import { BrowserRouter, Routes, Route, Link, Outlet } from "react-router-dom";  
  
// Approach #1  
function App() {  
  return (  
    <BrowserRouter>  
      <Routes>  
        <Route path="/" element={<Home />} />  
        <Route path="profile/*" element={<Profile />} />  
      </Routes>  
    </BrowserRouter>  
  );  
}  
  
function Profile() {  
  return (  
    <div>  
      <nav>  
        <Link to="me">My Profile</Link>  
      </nav>  
  
      <Routes>  
        <Route path="me" element={<MyProfile />} />  
        <Route path=":id" element={<OthersProfile />} />  
      </Routes>  
    </div>  
  );  
}  
  
// Approach #2  
// You can also define all  
// <Route> in a single place  
function App() {  
  return (  
    <BrowserRouter>  
      <Routes>  
        <Route path="/" element={<Home />} />  
        <Route path="profile" element={<Profile />}>  
          <Route path=":id" element={<MyProfile />} />  
          <Route path="me" element={<OthersProfile />} />  
        </Route>  
      </Routes>  
    </BrowserRouter>  
  );  
}  
  
function Profile() {  
  return (  
    <div>  
      <nav>  
        <Link to="me">My Profile</Link>  
      </nav>  
  
      <Outlet />  
    </div>  
  );  
}

Note that the Outlet is the Element of the render sub route.

useNavigate instead of useHistory

In v5, the active jump route can be used for history.push and other operations through useHistory

// v5  
import { useHistory } from "react-router-dom";  
  
function MyButton() {  
  let history = useHistory();  
  function handleClick() {  
    history.push("/home");  
  }  
  return <button onClick={handleClick}>Submit</button>;  
}

In v6, you can directly implement this common operation through useNavigate:

// v6  
import { useNavigate } from "react-router-dom";  
  
function MyButton() {  
  let navigate = useNavigate();  
  function handleClick() {  
    navigate("/home");  
  }  
  return <button onClick={handleClick}>Submit</button>;  
}

The history is encapsulated in the react router. If history.replace is needed, you can specify it with the {replace: true} parameter:

// v5  
history.push("/home");  
history.replace("/home");  
  
// v6  
navigate("/home");  
navigate("/home", { replace: true });

Smaller volume 8kb

Due to the almost reconstruction of the code, the volume of the v6 version is reduced from 20kb to 8kb after compression.

3 intensive reading

There is a core idea in the source code of react router V6. The author will share it with you, which is very helpful for some framework development. Let's take a look at the code excerpt of useRoutes:

export function useRoutes(routes, basename = "", caseSensitive = false) {  
  let {  
    params: parentParams,  
    pathname: parentPathname,  
    route: parentRoute  
  } = React.useContext(RouteContext);  
  
  if (warnAboutMissingTrailingSplatAt) {  
    // ...  
  }  
  
  basename = basename ? joinPaths([parentPathname, basename]) : parentPathname;  
  
  let navigate = useNavigate();  
  let location = useLocation();  
  let matches = React.useMemo(  
    () => matchRoutes(routes, location, basename, caseSensitive),  
    [routes, location, basename, caseSensitive]  
  );  
  
  // ...  
  
  // Otherwise render an element.  
  let element = matches.reduceRight((outlet, { params, pathname, route }) => {  
    return (  
      <RouteContext.Provider  
        children={route.element}  
        value={{  
          outlet,  
          params: readOnly({ ...parentParams, ...params }),  
          pathname: joinPaths([basename, pathname]),  
          route  
        }}  
      />  
    );  
  }, null);  
  
  return element;  
}

As you can see, with React.Context, the v6 version wraps a layer of RouteContext when rendering each routing element.

For more convenient routing nesting:

In v6 version, useRouteMatch is omitted, and path is directly used to represent relative path.

This is achieved by using this scheme. Because each layer of route file is wrapped with Context, the path of the previous layer can be obtained at each layer. Therefore, when splicing routes, it can be completely implemented by the framework, without the need for users to splice them in advance when invoking.

Taking useNavigate as an example, some people think that the encapsulation of navigation only stays in the formal layer, but it also has encapsulation in function. For example, if the incoming path is a relative path, it will switch according to the current route. Here is the excerpt of useNavigate Code:

export function useNavigate() {  
  let { history, pending } = React.useContext(LocationContext);  
  let { pathname } = React.useContext(RouteContext);  
  
  let navigate = React.useCallback(  
    (to, { replace, state } = {}) => {  
      if (typeof to === "number") {  
        history.go(to);  
      } else {  
        let relativeTo = resolveLocation(to, pathname);  
  
        let method = !!replace || pending ? "replace" : "push";  
        history[method](relativeTo, state);  
      }  
    },  
    [history, pending, pathname]  
  );  
  
  return navigate;  
}

You can see that RouteContext is used to get the current pathname, and to and pathname are spliced according to resolveLocation, and pathname is provided through RouteContext.Provider.

Using multi-layer Context Provider skillfully

Many times, we use Context to stay at the level of one Provider and multiple usecontexts, which is the most basic usage of Context. However, we believe that after reading React Router v6, we can find more usage of Context: multi-layer Context Provider.

Although it is said that there are multiple layers of Context Provider that will adopt the principle of recent coverage, this is not only a function to avoid errors, we can use this function to achieve such improvements as React Router v6.

To illustrate this feature in more detail, here is another specific example: for example, when building a rendering engine, each component has an id, but this id does not show up in the props of the component:

const Input = () => {  
  // The Input component will automatically generate an id in the canvas, but this id component cannot be obtained through props  
};

At this time, if we allow another sub element to be created inside the Input component and want the id of this sub element to be derived from Input, we may need the user to do this:

const Input = ({ id }) => {  
  return <ComponentLoader id={id + "1"} />;  
};

There are two problems in doing so:

  1. Exposing the id to the Input component violates the simplicity of the previous design.
  2. The component needs to assemble the id, which is very troublesome.

The problems encountered here are the same as those encountered by React Router. Can we simplify the code as follows, but the function remains the same?

const Input = () => {  
  return <ComponentLoader id="1" />;  
};

The answer is yes, we can use Context to implement this solution. The key point is to render Input, but the component container needs to wrap a Provider:

const ComponentLoader = ({ id, element }) => {  
  <Context.Provider value={{ id }}>{element}</Context.Provider>;  
};

For internal components, the id obtained by calling useContext at different levels is different, which is exactly the effect we want:

const ComponentLoader = ({id,element}) => {  
  const { id: parentId } = useContext(Context)  
  
  <Context.Provider value={{ id: parentId + id }}>  
    {element}  
  </Context.Provider>  
}

In this way, the < componentloader id = "1" / > we call inside Input actually splices the actual ID 01, which is completely thrown to the external engine layer for processing, and the user does not need to manually splice.

4 Summary

After the React Router v6 is fully refactored with Hooks, it not only simplifies the code, but also becomes more useful. When the official version is released, it can be upgraded quickly.

In addition, from these optimizations made by React Router v6, we have discovered more ingenious use of Context from the source code. I hope this method can help you to apply it to other more complex project designs. Thank you for your reference- http://bjbsair.com/2020-04-01/tech-info/18408.html

Pictures from the Internet

1 Introduction

The version of react route V6 alpha has been released. This week, we will analyze the changes through the article a snake peak at react route V6.

React Router v6: https://github.com/ReactTraining/react-router

A Sneak Peek at React Router v6 : https://alligator.io/react/react-router-v6/

2 Overview

Renamed

A painless change makes API naming more standard.

// v5  
import { BrowserRouter, Switch, Route } from "react-router-dom";  
  
function App() {  
  return (  
    <BrowserRouter>  
      <Switch>  
        <Route exact path="/">  
          <Home />  
        </Route>  
        <Route path="/profile">  
          <Profile />  
        </Route>  
      </Switch>  
    </BrowserRouter>  
  );  
}

In the V6 version of react router, directly use Routes instead of Switch:

// v6  
import { BrowserRouter, Routes, Route } from "react-router-dom";  
  
function App() {  
  return (  
    <BrowserRouter>  
      <Routes>  
        <Route path="/" element={<Home />} />  
        <Route path="profile/*" element={<Profile />} />  
      </Routes>  
    </BrowserRouter>  
  );  
}

upgrade

In v5 version, it is not very intuitive to pass parameters to components. You need to use RenderProps to pass through routeProps:

import Profile from './Profile';  
  
// v5  
<Route path=":userId" component={Profile} />  
<Route  
  path=":userId"  
  render={routeProps => (  
    <Profile {...routeProps} animate={true} />  
  )}  
/>  
  
// v6  
<Route path=":userId" element={<Profile />} />  
<Route path=":userId" element={<Profile animate={true} />} />

In v6, the render and component schemes are combined into element schemes, which can easily pass props without passing through the roteProps parameters.

More convenient nested routing

In v5, nested routes need to get match through useRouteMatch, and sub routes can be realized through match.path splicing:

// v5  
import {  
  BrowserRouter,  
  Switch,  
  Route,  
  Link,  
  useRouteMatch  
} from "react-router-dom";  
  
function App() {  
  return (  
    <BrowserRouter>  
      <Switch>  
        <Route exact path="/" component={Home} />  
        <Route path="/profile" component={Profile} />  
      </Switch>  
    </BrowserRouter>  
  );  
}  
  
function Profile() {  
  let match = useRouteMatch();  
  
  return (  
    <div>  
      <nav>  
        <Link to={`${match.url}/me`}>My Profile</Link>  
      </nav>  
  
      <Switch>  
        <Route path={`${match.path}/me`}>  
          <MyProfile />  
        </Route>  
        <Route path={`${match.path}/:id`}>  
          <OthersProfile />  
        </Route>  
      </Switch>  
    </div>  
  );  
}

In the v6 version, the useRouteMatch step is omitted, and the relative path can be directly represented by path:

// v6  
import { BrowserRouter, Routes, Route, Link, Outlet } from "react-router-dom";  
  
// Approach #1  
function App() {  
  return (  
    <BrowserRouter>  
      <Routes>  
        <Route path="/" element={<Home />} />  
        <Route path="profile/*" element={<Profile />} />  
      </Routes>  
    </BrowserRouter>  
  );  
}  
  
function Profile() {  
  return (  
    <div>  
      <nav>  
        <Link to="me">My Profile</Link>  
      </nav>  
  
      <Routes>  
        <Route path="me" element={<MyProfile />} />  
        <Route path=":id" element={<OthersProfile />} />  
      </Routes>  
    </div>  
  );  
}  
  
// Approach #2  
// You can also define all  
// <Route> in a single place  
function App() {  
  return (  
    <BrowserRouter>  
      <Routes>  
        <Route path="/" element={<Home />} />  
        <Route path="profile" element={<Profile />}>  
          <Route path=":id" element={<MyProfile />} />  
          <Route path="me" element={<OthersProfile />} />  
        </Route>  
      </Routes>  
    </BrowserRouter>  
  );  
}  
  
function Profile() {  
  return (  
    <div>  
      <nav>  
        <Link to="me">My Profile</Link>  
      </nav>  
  
      <Outlet />  
    </div>  
  );  
}

Note that the Outlet is the Element of the render sub route.

useNavigate instead of useHistory

In v5, the active jump route can be used for history.push and other operations through useHistory

// v5  
import { useHistory } from "react-router-dom";  
  
function MyButton() {  
  let history = useHistory();  
  function handleClick() {  
    history.push("/home");  
  }  
  return <button onClick={handleClick}>Submit</button>;  
}

In v6, you can directly implement this common operation through useNavigate:

// v6  
import { useNavigate } from "react-router-dom";  
  
function MyButton() {  
  let navigate = useNavigate();  
  function handleClick() {  
    navigate("/home");  
  }  
  return <button onClick={handleClick}>Submit</button>;  
}

The history is encapsulated in the react router. If history.replace is needed, you can specify it with the {replace: true} parameter:

// v5  
history.push("/home");  
history.replace("/home");  
  
// v6  
navigate("/home");  
navigate("/home", { replace: true });

Smaller volume 8kb

Due to the almost reconstruction of the code, the volume of the v6 version is reduced from 20kb to 8kb after compression.

3 intensive reading

There is a core idea in the source code of react router V6. The author will share it with you, which is very helpful for some framework development. Let's take a look at the code excerpt of useRoutes:

export function useRoutes(routes, basename = "", caseSensitive = false) {  
  let {  
    params: parentParams,  
    pathname: parentPathname,  
    route: parentRoute  
  } = React.useContext(RouteContext);  
  
  if (warnAboutMissingTrailingSplatAt) {  
    // ...  
  }  
  
  basename = basename ? joinPaths([parentPathname, basename]) : parentPathname;  
  
  let navigate = useNavigate();  
  let location = useLocation();  
  let matches = React.useMemo(  
    () => matchRoutes(routes, location, basename, caseSensitive),  
    [routes, location, basename, caseSensitive]  
  );  
  
  // ...  
  
  // Otherwise render an element.  
  let element = matches.reduceRight((outlet, { params, pathname, route }) => {  
    return (  
      <RouteContext.Provider  
        children={route.element}  
        value={{  
          outlet,  
          params: readOnly({ ...parentParams, ...params }),  
          pathname: joinPaths([basename, pathname]),  
          route  
        }}  
      />  
    );  
  }, null);  
  
  return element;  
}

As you can see, with React.Context, the v6 version wraps a layer of RouteContext when rendering each routing element.

For more convenient routing nesting:

In v6 version, useRouteMatch is omitted, and path is directly used to represent relative path.

This is achieved by using this scheme. Because each layer of route file is wrapped with Context, the path of the previous layer can be obtained at each layer. Therefore, when splicing routes, it can be completely implemented by the framework, without the need for users to splice them in advance when invoking.

Taking useNavigate as an example, some people think that the encapsulation of navigation only stays in the formal layer, but it also has encapsulation in function. For example, if the incoming path is a relative path, it will switch according to the current route. Here is the excerpt of useNavigate Code:

export function useNavigate() {  
  let { history, pending } = React.useContext(LocationContext);  
  let { pathname } = React.useContext(RouteContext);  
  
  let navigate = React.useCallback(  
    (to, { replace, state } = {}) => {  
      if (typeof to === "number") {  
        history.go(to);  
      } else {  
        let relativeTo = resolveLocation(to, pathname);  
  
        let method = !!replace || pending ? "replace" : "push";  
        history[method](relativeTo, state);  
      }  
    },  
    [history, pending, pathname]  
  );  
  
  return navigate;  
}

You can see that RouteContext is used to get the current pathname, and to and pathname are spliced according to resolveLocation, and pathname is provided through RouteContext.Provider.

Using multi-layer Context Provider skillfully

Many times, we use Context to stay at the level of one Provider and multiple usecontexts, which is the most basic usage of Context. However, we believe that after reading React Router v6, we can find more usage of Context: multi-layer Context Provider.

Although it is said that there are multiple layers of Context Provider that will adopt the principle of recent coverage, this is not only a function to avoid errors, we can use this function to achieve such improvements as React Router v6.

To illustrate this feature in more detail, here is another specific example: for example, when building a rendering engine, each component has an id, but this id does not show up in the props of the component:

const Input = () => {  
  // The Input component will automatically generate an id in the canvas, but this id component cannot be obtained through props  
};

At this time, if we allow another sub element to be created inside the Input component and want the id of this sub element to be derived from Input, we may need the user to do this:

const Input = ({ id }) => {  
  return <ComponentLoader id={id + "1"} />;  
};

There are two problems in doing so:

  1. Exposing the id to the Input component violates the simplicity of the previous design.
  2. The component needs to assemble the id, which is very troublesome.

The problems encountered here are the same as those encountered by React Router. Can we simplify the code as follows, but the function remains the same?

const Input = () => {  
  return <ComponentLoader id="1" />;  
};

The answer is yes, we can use Context to implement this solution. The key point is to render Input, but the component container needs to wrap a Provider:

const ComponentLoader = ({ id, element }) => {  
  <Context.Provider value={{ id }}>{element}</Context.Provider>;  
};

For internal components, the id obtained by calling useContext at different levels is different, which is exactly the effect we want:

const ComponentLoader = ({id,element}) => {  
  const { id: parentId } = useContext(Context)  
  
  <Context.Provider value={{ id: parentId + id }}>  
    {element}  
  </Context.Provider>  
}

In this way, the < componentloader id = "1" / > we call inside Input actually splices the actual ID 01, which is completely thrown to the external engine layer for processing, and the user does not need to manually splice.

4 Summary

After the React Router v6 is fully refactored with Hooks, it not only simplifies the code, but also becomes more useful. When the official version is released, it can be upgraded quickly.

In addition, from these optimizations made by React Router v6, we have discovered more ingenious use of Context from the source code. I hope this method can help you to apply it to other more complex project designs. Thank you for your reference- http://bjbsair.com/2020-04-01/tech-info/18408.html

Pictures from the Internet

1 Introduction

The version of react route V6 alpha has been released. This week, we will analyze the changes through the article a snake peak at react route V6.

React Router v6: https://github.com/ReactTraining/react-router

A Sneak Peek at React Router v6 : https://alligator.io/react/react-router-v6/

2 Overview

Renamed

A painless change makes API naming more standard.

// v5  
import { BrowserRouter, Switch, Route } from "react-router-dom";  
  
function App() {  
  return (  
    <BrowserRouter>  
      <Switch>  
        <Route exact path="/">  
          <Home />  
        </Route>  
        <Route path="/profile">  
          <Profile />  
        </Route>  
      </Switch>  
    </BrowserRouter>  
  );  
}

In the V6 version of react router, directly use Routes instead of Switch:

// v6  
import { BrowserRouter, Routes, Route } from "react-router-dom";  
  
function App() {  
  return (  
    <BrowserRouter>  
      <Routes>  
        <Route path="/" element={<Home />} />  
        <Route path="profile/*" element={<Profile />} />  
      </Routes>  
    </BrowserRouter>  
  );  
}

upgrade

In v5 version, it is not very intuitive to pass parameters to components. You need to use RenderProps to pass through routeProps:

import Profile from './Profile';  
  
// v5  
<Route path=":userId" component={Profile} />  
<Route  
  path=":userId"  
  render={routeProps => (  
    <Profile {...routeProps} animate={true} />  
  )}  
/>  
  
// v6  
<Route path=":userId" element={<Profile />} />  
<Route path=":userId" element={<Profile animate={true} />} />

In v6, the render and component schemes are combined into element schemes, which can easily pass props without passing through the roteProps parameters.

More convenient nested routing

In v5, nested routes need to get match through useRouteMatch, and sub routes can be realized through match.path splicing:

// v5  
import {  
  BrowserRouter,  
  Switch,  
  Route,  
  Link,  
  useRouteMatch  
} from "react-router-dom";  
  
function App() {  
  return (  
    <BrowserRouter>  
      <Switch>  
        <Route exact path="/" component={Home} />  
        <Route path="/profile" component={Profile} />  
      </Switch>  
    </BrowserRouter>  
  );  
}  
  
function Profile() {  
  let match = useRouteMatch();  
  
  return (  
    <div>  
      <nav>  
        <Link to={`${match.url}/me`}>My Profile</Link>  
      </nav>  
  
      <Switch>  
        <Route path={`${match.path}/me`}>  
          <MyProfile />  
        </Route>  
        <Route path={`${match.path}/:id`}>  
          <OthersProfile />  
        </Route>  
      </Switch>  
    </div>  
  );  
}

In the v6 version, the useRouteMatch step is omitted, and the relative path can be directly represented by path:

// v6  
import { BrowserRouter, Routes, Route, Link, Outlet } from "react-router-dom";  
  
// Approach #1  
function App() {  
  return (  
    <BrowserRouter>  
      <Routes>  
        <Route path="/" element={<Home />} />  
        <Route path="profile/*" element={<Profile />} />  
      </Routes>  
    </BrowserRouter>  
  );  
}  
  
function Profile() {  
  return (  
    <div>  
      <nav>  
        <Link to="me">My Profile</Link>  
      </nav>  
  
      <Routes>  
        <Route path="me" element={<MyProfile />} />  
        <Route path=":id" element={<OthersProfile />} />  
      </Routes>  
    </div>  
  );  
}  
  
// Approach #2  
// You can also define all  
// <Route> in a single place  
function App() {  
  return (  
    <BrowserRouter>  
      <Routes>  
        <Route path="/" element={<Home />} />  
        <Route path="profile" element={<Profile />}>  
          <Route path=":id" element={<MyProfile />} />  
          <Route path="me" element={<OthersProfile />} />  
        </Route>  
      </Routes>  
    </BrowserRouter>  
  );  
}  
  
function Profile() {  
  return (  
    <div>  
      <nav>  
        <Link to="me">My Profile</Link>  
      </nav>  
  
      <Outlet />  
    </div>  
  );  
}

Note that the Outlet is the Element of the render sub route.

useNavigate instead of useHistory

In v5, the active jump route can be used for history.push and other operations through useHistory

// v5  
import { useHistory } from "react-router-dom";  
  
function MyButton() {  
  let history = useHistory();  
  function handleClick() {  
    history.push("/home");  
  }  
  return <button onClick={handleClick}>Submit</button>;  
}

In v6, you can directly implement this common operation through useNavigate:

// v6  
import { useNavigate } from "react-router-dom";  
  
function MyButton() {  
  let navigate = useNavigate();  
  function handleClick() {  
    navigate("/home");  
  }  
  return <button onClick={handleClick}>Submit</button>;  
}

The history is encapsulated in the react router. If history.replace is needed, you can specify it with the {replace: true} parameter:

// v5  
history.push("/home");  
history.replace("/home");  
  
// v6  
navigate("/home");  
navigate("/home", { replace: true });

Smaller volume 8kb

Due to the almost reconstruction of the code, the volume of the v6 version is reduced from 20kb to 8kb after compression.

3 intensive reading

There is a core idea in the source code of react router V6. The author will share it with you, which is very helpful for some framework development. Let's take a look at the code excerpt of useRoutes:

export function useRoutes(routes, basename = "", caseSensitive = false) {  
  let {  
    params: parentParams,  
    pathname: parentPathname,  
    route: parentRoute  
  } = React.useContext(RouteContext);  
  
  if (warnAboutMissingTrailingSplatAt) {  
    // ...  
  }  
  
  basename = basename ? joinPaths([parentPathname, basename]) : parentPathname;  
  
  let navigate = useNavigate();  
  let location = useLocation();  
  let matches = React.useMemo(  
    () => matchRoutes(routes, location, basename, caseSensitive),  
    [routes, location, basename, caseSensitive]  
  );  
  
  // ...  
  
  // Otherwise render an element.  
  let element = matches.reduceRight((outlet, { params, pathname, route }) => {  
    return (  
      <RouteContext.Provider  
        children={route.element}  
        value={{  
          outlet,  
          params: readOnly({ ...parentParams, ...params }),  
          pathname: joinPaths([basename, pathname]),  
          route  
        }}  
      />  
    );  
  }, null);  
  
  return element;  
}

As you can see, with React.Context, the v6 version wraps a layer of RouteContext when rendering each routing element.

For more convenient routing nesting:

In v6 version, useRouteMatch is omitted, and path is directly used to represent relative path.

This is achieved by using this scheme. Because each layer of route file is wrapped with Context, the path of the previous layer can be obtained at each layer. Therefore, when splicing routes, it can be completely implemented by the framework, without the need for users to splice them in advance when invoking.

Taking useNavigate as an example, some people think that the encapsulation of navigation only stays in the formal layer, but it also has encapsulation in function. For example, if the incoming path is a relative path, it will switch according to the current route. Here is the excerpt of useNavigate Code:

export function useNavigate() {  
  let { history, pending } = React.useContext(LocationContext);  
  let { pathname } = React.useContext(RouteContext);  
  
  let navigate = React.useCallback(  
    (to, { replace, state } = {}) => {  
      if (typeof to === "number") {  
        history.go(to);  
      } else {  
        let relativeTo = resolveLocation(to, pathname);  
  
        let method = !!replace || pending ? "replace" : "push";  
        history[method](relativeTo, state);  
      }  
    },  
    [history, pending, pathname]  
  );  
  
  return navigate;  
}

You can see that RouteContext is used to get the current pathname, and to and pathname are spliced according to resolveLocation, and pathname is provided through RouteContext.Provider.

Using multi-layer Context Provider skillfully

Many times, we use Context to stay at the level of one Provider and multiple usecontexts, which is the most basic usage of Context. However, we believe that after reading React Router v6, we can find more usage of Context: multi-layer Context Provider.

Although it is said that there are multiple layers of Context Provider that will adopt the principle of recent coverage, this is not only a function to avoid errors, we can use this function to achieve such improvements as React Router v6.

To illustrate this feature in more detail, here is another specific example: for example, when building a rendering engine, each component has an id, but this id does not show up in the props of the component:

const Input = () => {  
  // The Input component will automatically generate an id in the canvas, but this id component cannot be obtained through props  
};

At this time, if we allow another sub element to be created inside the Input component and want the id of this sub element to be derived from Input, we may need the user to do this:

const Input = ({ id }) => {  
  return <ComponentLoader id={id + "1"} />;  
};

There are two problems in doing so:

  1. Exposing the id to the Input component violates the simplicity of the previous design.
  2. The component needs to assemble the id, which is very troublesome.

The problems encountered here are the same as those encountered by React Router. Can we simplify the code as follows, but the function remains the same?

const Input = () => {  
  return <ComponentLoader id="1" />;  
};

The answer is yes, we can use Context to implement this solution. The key point is to render Input, but the component container needs to wrap a Provider:

const ComponentLoader = ({ id, element }) => {  
  <Context.Provider value={{ id }}>{element}</Context.Provider>;  
};

For internal components, the id obtained by calling useContext at different levels is different, which is exactly the effect we want:

const ComponentLoader = ({id,element}) => {  
  const { id: parentId } = useContext(Context)  
  
  <Context.Provider value={{ id: parentId + id }}>  
    {element}  
  </Context.Provider>  
}

In this way, the < componentloader id = "1" / > we call inside Input actually splices the actual ID 01, which is completely thrown to the external engine layer for processing, and the user does not need to manually splice.

4 Summary

After the React Router v6 is fully refactored with Hooks, it not only simplifies the code, but also becomes more useful. When the official version is released, it can be upgraded quickly.

In addition, from these optimizations made by React Router v6, we have discovered more ingenious use of Context from the source code. I hope this method can help you to apply it to other more complex project designs. Thank you for your reference- http://bjbsair.com/2020-04-01/tech-info/18408.html

Pictures from the Internet

1 Introduction

The version of react route V6 alpha has been released. This week, we will analyze the changes through the article a snake peak at react route V6.

React Router v6: https://github.com/ReactTraining/react-router

A Sneak Peek at React Router v6 : https://alligator.io/react/react-router-v6/

2 Overview

Renamed

A painless change makes API naming more standard.

// v5  
import { BrowserRouter, Switch, Route } from "react-router-dom";  
  
function App() {  
  return (  
    <BrowserRouter>  
      <Switch>  
        <Route exact path="/">  
          <Home />  
        </Route>  
        <Route path="/profile">  
          <Profile />  
        </Route>  
      </Switch>  
    </BrowserRouter>  
  );  
}

In the V6 version of react router, directly use Routes instead of Switch:

// v6  
import { BrowserRouter, Routes, Route } from "react-router-dom";  
  
function App() {  
  return (  
    <BrowserRouter>  
      <Routes>  
        <Route path="/" element={<Home />} />  
        <Route path="profile/*" element={<Profile />} />  
      </Routes>  
    </BrowserRouter>  
  );  
}

upgrade

In v5 version, it is not very intuitive to pass parameters to components. You need to use RenderProps to pass through routeProps:

import Profile from './Profile';  
  
// v5  
<Route path=":userId" component={Profile} />  
<Route  
  path=":userId"  
  render={routeProps => (  
    <Profile {...routeProps} animate={true} />  
  )}  
/>  
  
// v6  
<Route path=":userId" element={<Profile />} />  
<Route path=":userId" element={<Profile animate={true} />} />

In v6, the render and component schemes are combined into element schemes, which can easily pass props without passing through the roteProps parameters.

More convenient nested routing

In v5, nested routes need to get match through useRouteMatch, and sub routes can be realized through match.path splicing:

// v5  
import {  
  BrowserRouter,  
  Switch,  
  Route,  
  Link,  
  useRouteMatch  
} from "react-router-dom";  
  
function App() {  
  return (  
    <BrowserRouter>  
      <Switch>  
        <Route exact path="/" component={Home} />  
        <Route path="/profile" component={Profile} />  
      </Switch>  
    </BrowserRouter>  
  );  
}  
  
function Profile() {  
  let match = useRouteMatch();  
  
  return (  
    <div>  
      <nav>  
        <Link to={`${match.url}/me`}>My Profile</Link>  
      </nav>  
  
      <Switch>  
        <Route path={`${match.path}/me`}>  
          <MyProfile />  
        </Route>  
        <Route path={`${match.path}/:id`}>  
          <OthersProfile />  
        </Route>  
      </Switch>  
    </div>  
  );  
}

In the v6 version, the useRouteMatch step is omitted, and the relative path can be directly represented by path:

// v6  
import { BrowserRouter, Routes, Route, Link, Outlet } from "react-router-dom";  
  
// Approach #1  
function App() {  
  return (  
    <BrowserRouter>  
      <Routes>  
        <Route path="/" element={<Home />} />  
        <Route path="profile/*" element={<Profile />} />  
      </Routes>  
    </BrowserRouter>  
  );  
}  
  
function Profile() {  
  return (  
    <div>  
      <nav>  
        <Link to="me">My Profile</Link>  
      </nav>  
  
      <Routes>  
        <Route path="me" element={<MyProfile />} />  
        <Route path=":id" element={<OthersProfile />} />  
      </Routes>  
    </div>  
  );  
}  
  
// Approach #2  
// You can also define all  
// <Route> in a single place  
function App() {  
  return (  
    <BrowserRouter>  
      <Routes>  
        <Route path="/" element={<Home />} />  
        <Route path="profile" element={<Profile />}>  
          <Route path=":id" element={<MyProfile />} />  
          <Route path="me" element={<OthersProfile />} />  
        </Route>  
      </Routes>  
    </BrowserRouter>  
  );  
}  
  
function Profile() {  
  return (  
    <div>  
      <nav>  
        <Link to="me">My Profile</Link>  
      </nav>  
  
      <Outlet />  
    </div>  
  );  
}

Note that the Outlet is the Element of the render sub route.

useNavigate instead of useHistory

In v5, the active jump route can be used for history.push and other operations through useHistory

// v5  
import { useHistory } from "react-router-dom";  
  
function MyButton() {  
  let history = useHistory();  
  function handleClick() {  
    history.push("/home");  
  }  
  return <button onClick={handleClick}>Submit</button>;  
}

In v6, you can directly implement this common operation through useNavigate:

// v6  
import { useNavigate } from "react-router-dom";  
  
function MyButton() {  
  let navigate = useNavigate();  
  function handleClick() {  
    navigate("/home");  
  }  
  return <button onClick={handleClick}>Submit</button>;  
}

The history is encapsulated in the react router. If history.replace is needed, you can specify it with the {replace: true} parameter:

// v5  
history.push("/home");  
history.replace("/home");  
  
// v6  
navigate("/home");  
navigate("/home", { replace: true });

Smaller volume 8kb

Due to the almost reconstruction of the code, the volume of the v6 version is reduced from 20kb to 8kb after compression.

3 intensive reading

There is a core idea in the source code of react router V6. The author will share it with you, which is very helpful for some framework development. Let's take a look at the code excerpt of useRoutes:

export function useRoutes(routes, basename = "", caseSensitive = false) {  
  let {  
    params: parentParams,  
    pathname: parentPathname,  
    route: parentRoute  
  } = React.useContext(RouteContext);  
  
  if (warnAboutMissingTrailingSplatAt) {  
    // ...  
  }  
  
  basename = basename ? joinPaths([parentPathname, basename]) : parentPathname;  
  
  let navigate = useNavigate();  
  let location = useLocation();  
  let matches = React.useMemo(  
    () => matchRoutes(routes, location, basename, caseSensitive),  
    [routes, location, basename, caseSensitive]  
  );  
  
  // ...  
  
  // Otherwise render an element.  
  let element = matches.reduceRight((outlet, { params, pathname, route }) => {  
    return (  
      <RouteContext.Provider  
        children={route.element}  
        value={{  
          outlet,  
          params: readOnly({ ...parentParams, ...params }),  
          pathname: joinPaths([basename, pathname]),  
          route  
        }}  
      />  
    );  
  }, null);  
  
  return element;  
}

As you can see, with React.Context, the v6 version wraps a layer of RouteContext when rendering each routing element.

For more convenient routing nesting:

In v6 version, useRouteMatch is omitted, and path is directly used to represent relative path.

This is achieved by using this scheme. Because each layer of route file is wrapped with Context, the path of the previous layer can be obtained at each layer. Therefore, when splicing routes, it can be completely implemented by the framework, without the need for users to splice them in advance when invoking.

Taking useNavigate as an example, some people think that the encapsulation of navigation only stays in the formal layer, but it also has encapsulation in function. For example, if the incoming path is a relative path, it will switch according to the current route. Here is the excerpt of useNavigate Code:

export function useNavigate() {  
  let { history, pending } = React.useContext(LocationContext);  
  let { pathname } = React.useContext(RouteContext);  
  
  let navigate = React.useCallback(  
    (to, { replace, state } = {}) => {  
      if (typeof to === "number") {  
        history.go(to);  
      } else {  
        let relativeTo = resolveLocation(to, pathname);  
  
        let method = !!replace || pending ? "replace" : "push";  
        history[method](relativeTo, state);  
      }  
    },  
    [history, pending, pathname]  
  );  
  
  return navigate;  
}

You can see that RouteContext is used to get the current pathname, and to and pathname are spliced according to resolveLocation, and pathname is provided through RouteContext.Provider.

Using multi-layer Context Provider skillfully

Many times, we use Context to stay at the level of one Provider and multiple usecontexts, which is the most basic usage of Context. However, we believe that after reading React Router v6, we can find more usage of Context: multi-layer Context Provider.

Although it is said that there are multiple layers of Context Provider that will adopt the principle of recent coverage, this is not only a function to avoid errors, we can use this function to achieve such improvements as React Router v6.

To illustrate this feature in more detail, here is another specific example: for example, when building a rendering engine, each component has an id, but this id does not show up in the props of the component:

const Input = () => {  
  // The Input component will automatically generate an id in the canvas, but this id component cannot be obtained through props  
};

At this time, if we allow another sub element to be created inside the Input component and want the id of this sub element to be derived from Input, we may need the user to do this:

const Input = ({ id }) => {  
  return <ComponentLoader id={id + "1"} />;  
};

There are two problems in doing so:

  1. Exposing the id to the Input component violates the simplicity of the previous design.
  2. The component needs to assemble the id, which is very troublesome.

The problems encountered here are the same as those encountered by React Router. Can we simplify the code as follows, but the function remains the same?

const Input = () => {  
  return <ComponentLoader id="1" />;  
};

The answer is yes, we can use Context to implement this solution. The key point is to render Input, but the component container needs to wrap a Provider:

const ComponentLoader = ({ id, element }) => {  
  <Context.Provider value={{ id }}>{element}</Context.Provider>;  
};

For internal components, the id obtained by calling useContext at different levels is different, which is exactly the effect we want:

const ComponentLoader = ({id,element}) => {  
  const { id: parentId } = useContext(Context)  
  
  <Context.Provider value={{ id: parentId + id }}>  
    {element}  
  </Context.Provider>  
}

In this way, the < componentloader id = "1" / > we call inside Input actually splices the actual ID 01, which is completely thrown to the external engine layer for processing, and the user does not need to manually splice.

4 Summary

After the React Router v6 is fully refactored with Hooks, it not only simplifies the code, but also becomes more useful. When the official version is released, it can be upgraded quickly.

In addition, from these optimizations made by React Router v6, we have discovered more ingenious use of Context from the source code. I hope this method can help you to apply it to other more complex project designs. Thank you for your reference- http://bjbsair.com/2020-04-01/tech-info/18408.html

Pictures from the Internet

1 Introduction

The version of react route V6 alpha has been released. This week, we will analyze the changes through the article a snake peak at react route V6.

React Router v6: https://github.com/ReactTraining/react-router

A Sneak Peek at React Router v6 : https://alligator.io/react/react-router-v6/

2 Overview

Renamed

A painless change makes API naming more standard.

// v5  
import { BrowserRouter, Switch, Route } from "react-router-dom";  
  
function App() {  
  return (  
    <BrowserRouter>  
      <Switch>  
        <Route exact path="/">  
          <Home />  
        </Route>  
        <Route path="/profile">  
          <Profile />  
        </Route>  
      </Switch>  
    </BrowserRouter>  
  );  
}

In the V6 version of react router, directly use Routes instead of Switch:

// v6  
import { BrowserRouter, Routes, Route } from "react-router-dom";  
  
function App() {  
  return (  
    <BrowserRouter>  
      <Routes>  
        <Route path="/" element={<Home />} />  
        <Route path="profile/*" element={<Profile />} />  
      </Routes>  
    </BrowserRouter>  
  );  
}

upgrade

In v5 version, it is not very intuitive to pass parameters to components. You need to use RenderProps to pass through routeProps:

import Profile from './Profile';  
  
// v5  
<Route path=":userId" component={Profile} />  
<Route  
  path=":userId"  
  render={routeProps => (  
    <Profile {...routeProps} animate={true} />  
  )}  
/>  
  
// v6  
<Route path=":userId" element={<Profile />} />  
<Route path=":userId" element={<Profile animate={true} />} />

In v6, the render and component schemes are combined into element schemes, which can easily pass props without passing through the roteProps parameters.

More convenient nested routing

In v5, nested routes need to get match through useRouteMatch, and sub routes can be realized through match.path splicing:

// v5  
import {  
  BrowserRouter,  
  Switch,  
  Route,  
  Link,  
  useRouteMatch  
} from "react-router-dom";  
  
function App() {  
  return (  
    <BrowserRouter>  
      <Switch>  
        <Route exact path="/" component={Home} />  
        <Route path="/profile" component={Profile} />  
      </Switch>  
    </BrowserRouter>  
  );  
}  
  
function Profile() {  
  let match = useRouteMatch();  
  
  return (  
    <div>  
      <nav>  
        <Link to={`${match.url}/me`}>My Profile</Link>  
      </nav>  
  
      <Switch>  
        <Route path={`${match.path}/me`}>  
          <MyProfile />  
        </Route>  
        <Route path={`${match.path}/:id`}>  
          <OthersProfile />  
        </Route>  
      </Switch>  
    </div>  
  );  
}

In the v6 version, the useRouteMatch step is omitted, and the relative path can be directly represented by path:

// v6  
import { BrowserRouter, Routes, Route, Link, Outlet } from "react-router-dom";  
  
// Approach #1  
function App() {  
  return (  
    <BrowserRouter>  
      <Routes>  
        <Route path="/" element={<Home />} />  
        <Route path="profile/*" element={<Profile />} />  
      </Routes>  
    </BrowserRouter>  
  );  
}  
  
function Profile() {  
  return (  
    <div>  
      <nav>  
        <Link to="me">My Profile</Link>  
      </nav>  
  
      <Routes>  
        <Route path="me" element={<MyProfile />} />  
        <Route path=":id" element={<OthersProfile />} />  
      </Routes>  
    </div>  
  );  
}  
  
// Approach #2  
// You can also define all  
// <Route> in a single place  
function App() {  
  return (  
    <BrowserRouter>  
      <Routes>  
        <Route path="/" element={<Home />} />  
        <Route path="profile" element={<Profile />}>  
          <Route path=":id" element={<MyProfile />} />  
          <Route path="me" element={<OthersProfile />} />  
        </Route>  
      </Routes>  
    </BrowserRouter>  
  );  
}  
  
function Profile() {  
  return (  
    <div>  
      <nav>  
        <Link to="me">My Profile</Link>  
      </nav>  
  
      <Outlet />  
    </div>  
  );  
}

Note that the Outlet is the Element of the render sub route.

useNavigate instead of useHistory

In v5, the active jump route can be used for history.push and other operations through useHistory

// v5  
import { useHistory } from "react-router-dom";  
  
function MyButton() {  
  let history = useHistory();  
  function handleClick() {  
    history.push("/home");  
  }  
  return <button onClick={handleClick}>Submit</button>;  
}

In v6, you can directly implement this common operation through useNavigate:

// v6  
import { useNavigate } from "react-router-dom";  
  
function MyButton() {  
  let navigate = useNavigate();  
  function handleClick() {  
    navigate("/home");  
  }  
  return <button onClick={handleClick}>Submit</button>;  
}

The history is encapsulated in the react router. If history.replace is needed, you can specify it with the {replace: true} parameter:

// v5  
history.push("/home");  
history.replace("/home");  
  
// v6  
navigate("/home");  
navigate("/home", { replace: true });

Smaller volume 8kb

Due to the almost reconstruction of the code, the volume of the v6 version is reduced from 20kb to 8kb after compression.

3 intensive reading

There is a core idea in the source code of react router V6. The author will share it with you, which is very helpful for some framework development. Let's take a look at the code excerpt of useRoutes:

export function useRoutes(routes, basename = "", caseSensitive = false) {  
  let {  
    params: parentParams,  
    pathname: parentPathname,  
    route: parentRoute  
  } = React.useContext(RouteContext);  
  
  if (warnAboutMissingTrailingSplatAt) {  
    // ...  
  }  
  
  basename = basename ? joinPaths([parentPathname, basename]) : parentPathname;  
  
  let navigate = useNavigate();  
  let location = useLocation();  
  let matches = React.useMemo(  
    () => matchRoutes(routes, location, basename, caseSensitive),  
    [routes, location, basename, caseSensitive]  
  );  
  
  // ...  
  
  // Otherwise render an element.  
  let element = matches.reduceRight((outlet, { params, pathname, route }) => {  
    return (  
      <RouteContext.Provider  
        children={route.element}  
        value={{  
          outlet,  
          params: readOnly({ ...parentParams, ...params }),  
          pathname: joinPaths([basename, pathname]),  
          route  
        }}  
      />  
    );  
  }, null);  
  
  return element;  
}

As you can see, with React.Context, the v6 version wraps a layer of RouteContext when rendering each routing element.

For more convenient routing nesting:

In v6 version, useRouteMatch is omitted, and path is directly used to represent relative path.

This is achieved by using this scheme. Because each layer of route file is wrapped with Context, the path of the previous layer can be obtained at each layer. Therefore, when splicing routes, it can be completely implemented by the framework, without the need for users to splice them in advance when invoking.

Taking useNavigate as an example, some people think that the encapsulation of navigation only stays in the formal layer, but it also has encapsulation in function. For example, if the incoming path is a relative path, it will switch according to the current route. Here is the excerpt of useNavigate Code:

export function useNavigate() {  
  let { history, pending } = React.useContext(LocationContext);  
  let { pathname } = React.useContext(RouteContext);  
  
  let navigate = React.useCallback(  
    (to, { replace, state } = {}) => {  
      if (typeof to === "number") {  
        history.go(to);  
      } else {  
        let relativeTo = resolveLocation(to, pathname);  
  
        let method = !!replace || pending ? "replace" : "push";  
        history[method](relativeTo, state);  
      }  
    },  
    [history, pending, pathname]  
  );  
  
  return navigate;  
}

You can see that RouteContext is used to get the current pathname, and to and pathname are spliced according to resolveLocation, and pathname is provided through RouteContext.Provider.

Using multi-layer Context Provider skillfully

Many times, we use Context to stay at the level of one Provider and multiple usecontexts, which is the most basic usage of Context. However, we believe that after reading React Router v6, we can find more usage of Context: multi-layer Context Provider.

Although it is said that there are multiple layers of Context Provider that will adopt the principle of recent coverage, this is not only a function to avoid errors, we can use this function to achieve such improvements as React Router v6.

To illustrate this feature in more detail, here is another specific example: for example, when building a rendering engine, each component has an id, but this id does not show up in the props of the component:

const Input = () => {  
  // The Input component will automatically generate an id in the canvas, but this id component cannot be obtained through props  
};

At this time, if we allow another sub element to be created inside the Input component and want the id of this sub element to be derived from Input, we may need the user to do this:

const Input = ({ id }) => {  
  return <ComponentLoader id={id + "1"} />;  
};

There are two problems in doing so:

  1. Exposing the id to the Input component violates the simplicity of the previous design.
  2. The component needs to assemble the id, which is very troublesome.

The problems encountered here are the same as those encountered by React Router. Can we simplify the code as follows, but the function remains the same?

const Input = () => {  
  return <ComponentLoader id="1" />;  
};

The answer is yes, we can use Context to implement this solution. The key point is to render Input, but the component container needs to wrap a Provider:

const ComponentLoader = ({ id, element }) => {  
  <Context.Provider value={{ id }}>{element}</Context.Provider>;  
};

For internal components, the id obtained by calling useContext at different levels is different, which is exactly the effect we want:

const ComponentLoader = ({id,element}) => {  
  const { id: parentId } = useContext(Context)  
  
  <Context.Provider value={{ id: parentId + id }}>  
    {element}  
  </Context.Provider>  
}

In this way, the < componentloader id = "1" / > we call inside Input actually splices the actual ID 01, which is completely thrown to the external engine layer for processing, and the user does not need to manually splice.

4 Summary

After the React Router v6 is fully refactored with Hooks, it not only simplifies the code, but also becomes more useful. When the official version is released, it can be upgraded quickly.

In addition, from these optimizations made by React Router v6, we have discovered more ingenious use of Context from the source code. I hope this method can help you to apply it to other more complex project designs. Thank you for your reference- http://bjbsair.com/2020-04-01/tech-info/18408.html

Pictures from the Internet

1 Introduction

The version of react route V6 alpha has been released. This week, we will analyze the changes through the article a snake peak at react route V6.

React Router v6: https://github.com/ReactTraining/react-router

A Sneak Peek at React Router v6 : https://alligator.io/react/react-router-v6/

2 Overview

Renamed

A painless change makes API naming more standard.

// v5  
import { BrowserRouter, Switch, Route } from "react-router-dom";  
  
function App() {  
  return (  
    <BrowserRouter>  
      <Switch>  
        <Route exact path="/">  
          <Home />  
        </Route>  
        <Route path="/profile">  
          <Profile />  
        </Route>  
      </Switch>  
    </BrowserRouter>  
  );  
}

In the V6 version of react router, directly use Routes instead of Switch:

// v6  
import { BrowserRouter, Routes, Route } from "react-router-dom";  
  
function App() {  
  return (  
    <BrowserRouter>  
      <Routes>  
        <Route path="/" element={<Home />} />  
        <Route path="profile/*" element={<Profile />} />  
      </Routes>  
    </BrowserRouter>  
  );  
}

upgrade

In v5 version, it is not very intuitive to pass parameters to components. You need to use RenderProps to pass through routeProps:

import Profile from './Profile';  
  
// v5  
<Route path=":userId" component={Profile} />  
<Route  
  path=":userId"  
  render={routeProps => (  
    <Profile {...routeProps} animate={true} />  
  )}  
/>  
  
// v6  
<Route path=":userId" element={<Profile />} />  
<Route path=":userId" element={<Profile animate={true} />} />

In v6, the render and component schemes are combined into element schemes, which can easily pass props without passing through the roteProps parameters.

More convenient nested routing

In v5, nested routes need to get match through useRouteMatch, and sub routes can be realized through match.path splicing:

// v5  
import {  
  BrowserRouter,  
  Switch,  
  Route,  
  Link,  
  useRouteMatch  
} from "react-router-dom";  
  
function App() {  
  return (  
    <BrowserRouter>  
      <Switch>  
        <Route exact path="/" component={Home} />  
        <Route path="/profile" component={Profile} />  
      </Switch>  
    </BrowserRouter>  
  );  
}  
  
function Profile() {  
  let match = useRouteMatch();  
  
  return (  
    <div>  
      <nav>  
        <Link to={`${match.url}/me`}>My Profile</Link>  
      </nav>  
  
      <Switch>  
        <Route path={`${match.path}/me`}>  
          <MyProfile />  
        </Route>  
        <Route path={`${match.path}/:id`}>  
          <OthersProfile />  
        </Route>  
      </Switch>  
    </div>  
  );  
}

In the v6 version, the useRouteMatch step is omitted, and the relative path can be directly represented by path:

// v6  
import { BrowserRouter, Routes, Route, Link, Outlet } from "react-router-dom";  
  
// Approach #1  
function App() {  
  return (  
    <BrowserRouter>  
      <Routes>  
        <Route path="/" element={<Home />} />  
        <Route path="profile/*" element={<Profile />} />  
      </Routes>  
    </BrowserRouter>  
  );  
}  
  
function Profile() {  
  return (  
    <div>  
      <nav>  
        <Link to="me">My Profile</Link>  
      </nav>  
  
      <Routes>  
        <Route path="me" element={<MyProfile />} />  
        <Route path=":id" element={<OthersProfile />} />  
      </Routes>  
    </div>  
  );  
}  
  
// Approach #2  
// You can also define all  
// <Route> in a single place  
function App() {  
  return (  
    <BrowserRouter>  
      <Routes>  
        <Route path="/" element={<Home />} />  
        <Route path="profile" element={<Profile />}>  
          <Route path=":id" element={<MyProfile />} />  
          <Route path="me" element={<OthersProfile />} />  
        </Route>  
      </Routes>  
    </BrowserRouter>  
  );  
}  
  
function Profile() {  
  return (  
    <div>  
      <nav>  
        <Link to="me">My Profile</Link>  
      </nav>  
  
      <Outlet />  
    </div>  
  );  
}

Note that the Outlet is the Element of the render sub route.

useNavigate instead of useHistory

In v5, the active jump route can be used for history.push and other operations through useHistory

// v5  
import { useHistory } from "react-router-dom";  
  
function MyButton() {  
  let history = useHistory();  
  function handleClick() {  
    history.push("/home");  
  }  
  return <button onClick={handleClick}>Submit</button>;  
}

In v6, you can directly implement this common operation through useNavigate:

// v6  
import { useNavigate } from "react-router-dom";  
  
function MyButton() {  
  let navigate = useNavigate();  
  function handleClick() {  
    navigate("/home");  
  }  
  return <button onClick={handleClick}>Submit</button>;  
}

The history is encapsulated in the react router. If history.replace is needed, you can specify it with the {replace: true} parameter:

// v5  
history.push("/home");  
history.replace("/home");  
  
// v6  
navigate("/home");  
navigate("/home", { replace: true });

Smaller volume 8kb

Due to the almost reconstruction of the code, the volume of the v6 version is reduced from 20kb to 8kb after compression.

3 intensive reading

There is a core idea in the source code of react router V6. The author will share it with you, which is very helpful for some framework development. Let's take a look at the code excerpt of useRoutes:

export function useRoutes(routes, basename = "", caseSensitive = false) {  
  let {  
    params: parentParams,  
    pathname: parentPathname,  
    route: parentRoute  
  } = React.useContext(RouteContext);  
  
  if (warnAboutMissingTrailingSplatAt) {  
    // ...  
  }  
  
  basename = basename ? joinPaths([parentPathname, basename]) : parentPathname;  
  
  let navigate = useNavigate();  
  let location = useLocation();  
  let matches = React.useMemo(  
    () => matchRoutes(routes, location, basename, caseSensitive),  
    [routes, location, basename, caseSensitive]  
  );  
  
  // ...  
  
  // Otherwise render an element.  
  let element = matches.reduceRight((outlet, { params, pathname, route }) => {  
    return (  
      <RouteContext.Provider  
        children={route.element}  
        value={{  
          outlet,  
          params: readOnly({ ...parentParams, ...params }),  
          pathname: joinPaths([basename, pathname]),  
          route  
        }}  
      />  
    );  
  }, null);  
  
  return element;  
}

As you can see, with React.Context, the v6 version wraps a layer of RouteContext when rendering each routing element.

For more convenient routing nesting:

In v6 version, useRouteMatch is omitted, and path is directly used to represent relative path.

This is achieved by using this scheme. Because each layer of route file is wrapped with Context, the path of the previous layer can be obtained at each layer. Therefore, when splicing routes, it can be completely implemented by the framework, without the need for users to splice them in advance when invoking.

Taking useNavigate as an example, some people think that the encapsulation of navigation only stays in the formal layer, but it also has encapsulation in function. For example, if the incoming path is a relative path, it will switch according to the current route. Here is the excerpt of useNavigate Code:

export function useNavigate() {  
  let { history, pending } = React.useContext(LocationContext);  
  let { pathname } = React.useContext(RouteContext);  
  
  let navigate = React.useCallback(  
    (to, { replace, state } = {}) => {  
      if (typeof to === "number") {  
        history.go(to);  
      } else {  
        let relativeTo = resolveLocation(to, pathname);  
  
        let method = !!replace || pending ? "replace" : "push";  
        history[method](relativeTo, state);  
      }  
    },  
    [history, pending, pathname]  
  );  
  
  return navigate;  
}

You can see that RouteContext is used to get the current pathname, and to and pathname are spliced according to resolveLocation, and pathname is provided through RouteContext.Provider.

Using multi-layer Context Provider skillfully

Many times, we use Context to stay at the level of one Provider and multiple usecontexts, which is the most basic usage of Context. However, we believe that after reading React Router v6, we can find more usage of Context: multi-layer Context Provider.

Although it is said that there are multiple layers of Context Provider that will adopt the principle of recent coverage, this is not only a function to avoid errors, we can use this function to achieve such improvements as React Router v6.

To illustrate this feature in more detail, here is another specific example: for example, when building a rendering engine, each component has an id, but this id does not show up in the props of the component:

const Input = () => {  
  // The Input component will automatically generate an id in the canvas, but this id component cannot be obtained through props  
};

At this time, if we allow another sub element to be created inside the Input component and want the id of this sub element to be derived from Input, we may need the user to do this:

const Input = ({ id }) => {  
  return <ComponentLoader id={id + "1"} />;  
};

There are two problems in doing so:

  1. Exposing the id to the Input component violates the simplicity of the previous design.
  2. The component needs to assemble the id, which is very troublesome.

The problems encountered here are the same as those encountered by React Router. Can we simplify the code as follows, but the function remains the same?

const Input = () => {  
  return <ComponentLoader id="1" />;  
};

The answer is yes, we can use Context to implement this solution. The key point is to render Input, but the component container needs to wrap a Provider:

const ComponentLoader = ({ id, element }) => {  
  <Context.Provider value={{ id }}>{element}</Context.Provider>;  
};

For internal components, the id obtained by calling useContext at different levels is different, which is exactly the effect we want:

const ComponentLoader = ({id,element}) => {  
  const { id: parentId } = useContext(Context)  
  
  <Context.Provider value={{ id: parentId + id }}>  
    {element}  
  </Context.Provider>  
}

In this way, the < componentloader id = "1" / > we call inside Input actually splices the actual ID 01, which is completely thrown to the external engine layer for processing, and the user does not need to manually splice.

4 Summary

After the React Router v6 is fully refactored with Hooks, it not only simplifies the code, but also becomes more useful. When the official version is released, it can be upgraded quickly.

In addition, from these optimizations made by React Router v6, we have discovered more ingenious use of Context from the source code. I hope this method can help you to apply it to other more complex project designs. Thank you for your reference- http://bjbsair.com/2020-04-01/tech-info/18408.html

Pictures from the Internet

1 Introduction

The version of react route V6 alpha has been released. This week, we will analyze the changes through the article a snake peak at react route V6.

React Router v6: https://github.com/ReactTraining/react-router

A Sneak Peek at React Router v6 : https://alligator.io/react/react-router-v6/

2 Overview

Renamed

A painless change makes API naming more standard.

// v5  
import { BrowserRouter, Switch, Route } from "react-router-dom";  
  
function App() {  
  return (  
    <BrowserRouter>  
      <Switch>  
        <Route exact path="/">  
          <Home />  
        </Route>  
        <Route path="/profile">  
          <Profile />  
        </Route>  
      </Switch>  
    </BrowserRouter>  
  );  
}

In the V6 version of react router, directly use Routes instead of Switch:

// v6  
import { BrowserRouter, Routes, Route } from "react-router-dom";  
  
function App() {  
  return (  
    <BrowserRouter>  
      <Routes>  
        <Route path="/" element={<Home />} />  
        <Route path="profile/*" element={<Profile />} />  
      </Routes>  
    </BrowserRouter>  
  );  
}

upgrade

In v5 version, it is not very intuitive to pass parameters to components. You need to use RenderProps to pass through routeProps:

import Profile from './Profile';  
  
// v5  
<Route path=":userId" component={Profile} />  
<Route  
  path=":userId"  
  render={routeProps => (  
    <Profile {...routeProps} animate={true} />  
  )}  
/>  
  
// v6  
<Route path=":userId" element={<Profile />} />  
<Route path=":userId" element={<Profile animate={true} />} />

In v6, the render and component schemes are combined into element schemes, which can easily pass props without passing through the roteProps parameters.

More convenient nested routing

In v5, nested routes need to get match through useRouteMatch, and sub routes can be realized through match.path splicing:

// v5  
import {  
  BrowserRouter,  
  Switch,  
  Route,  
  Link,  
  useRouteMatch  
} from "react-router-dom";  
  
function App() {  
  return (  
    <BrowserRouter>  
      <Switch>  
        <Route exact path="/" component={Home} />  
        <Route path="/profile" component={Profile} />  
      </Switch>  
    </BrowserRouter>  
  );  
}  
  
function Profile() {  
  let match = useRouteMatch();  
  
  return (  
    <div>  
      <nav>  
        <Link to={`${match.url}/me`}>My Profile</Link>  
      </nav>  
  
      <Switch>  
        <Route path={`${match.path}/me`}>  
          <MyProfile />  
        </Route>  
        <Route path={`${match.path}/:id`}>  
          <OthersProfile />  
        </Route>  
      </Switch>  
    </div>  
  );  
}

In the v6 version, the useRouteMatch step is omitted, and the relative path can be directly represented by path:

// v6  
import { BrowserRouter, Routes, Route, Link, Outlet } from "react-router-dom";  
  
// Approach #1  
function App() {  
  return (  
    <BrowserRouter>  
      <Routes>  
        <Route path="/" element={<Home />} />  
        <Route path="profile/*" element={<Profile />} />  
      </Routes>  
    </BrowserRouter>  
  );  
}  
  
function Profile() {  
  return (  
    <div>  
      <nav>  
        <Link to="me">My Profile</Link>  
      </nav>  
  
      <Routes>  
        <Route path="me" element={<MyProfile />} />  
        <Route path=":id" element={<OthersProfile />} />  
      </Routes>  
    </div>  
  );  
}  
  
// Approach #2  
// You can also define all  
// <Route> in a single place  
function App() {  
  return (  
    <BrowserRouter>  
      <Routes>  
        <Route path="/" element={<Home />} />  
        <Route path="profile" element={<Profile />}>  
          <Route path=":id" element={<MyProfile />} />  
          <Route path="me" element={<OthersProfile />} />  
        </Route>  
      </Routes>  
    </BrowserRouter>  
  );  
}  
  
function Profile() {  
  return (  
    <div>  
      <nav>  
        <Link to="me">My Profile</Link>  
      </nav>  
  
      <Outlet />  
    </div>  
  );  
}

Note that the Outlet is the Element of the render sub route.

useNavigate instead of useHistory

In v5, the active jump route can be used for history.push and other operations through useHistory

// v5  
import { useHistory } from "react-router-dom";  
  
function MyButton() {  
  let history = useHistory();  
  function handleClick() {  
    history.push("/home");  
  }  
  return <button onClick={handleClick}>Submit</button>;  
}

In v6, you can directly implement this common operation through useNavigate:

// v6  
import { useNavigate } from "react-router-dom";  
  
function MyButton() {  
  let navigate = useNavigate();  
  function handleClick() {  
    navigate("/home");  
  }  
  return <button onClick={handleClick}>Submit</button>;  
}

The history is encapsulated in the react router. If history.replace is needed, you can specify it with the {replace: true} parameter:

// v5  
history.push("/home");  
history.replace("/home");  
  
// v6  
navigate("/home");  
navigate("/home", { replace: true });

Smaller volume 8kb

Due to the almost reconstruction of the code, the volume of the v6 version is reduced from 20kb to 8kb after compression.

3 intensive reading

There is a core idea in the source code of react router V6. The author will share it with you, which is very helpful for some framework development. Let's take a look at the code excerpt of useRoutes:

export function useRoutes(routes, basename = "", caseSensitive = false) {  
  let {  
    params: parentParams,  
    pathname: parentPathname,  
    route: parentRoute  
  } = React.useContext(RouteContext);  
  
  if (warnAboutMissingTrailingSplatAt) {  
    // ...  
  }  
  
  basename = basename ? joinPaths([parentPathname, basename]) : parentPathname;  
  
  let navigate = useNavigate();  
  let location = useLocation();  
  let matches = React.useMemo(  
    () => matchRoutes(routes, location, basename, caseSensitive),  
    [routes, location, basename, caseSensitive]  
  );  
  
  // ...  
  
  // Otherwise render an element.  
  let element = matches.reduceRight((outlet, { params, pathname, route }) => {  
    return (  
      <RouteContext.Provider  
        children={route.element}  
        value={{  
          outlet,  
          params: readOnly({ ...parentParams, ...params }),  
          pathname: joinPaths([basename, pathname]),  
          route  
        }}  
      />  
    );  
  }, null);  
  
  return element;  
}

As you can see, with React.Context, the v6 version wraps a layer of RouteContext when rendering each routing element.

For more convenient routing nesting:

In v6 version, useRouteMatch is omitted, and path is directly used to represent relative path.

This is achieved by using this scheme. Because each layer of route file is wrapped with Context, the path of the previous layer can be obtained at each layer. Therefore, when splicing routes, it can be completely implemented by the framework, without the need for users to splice them in advance when invoking.

Taking useNavigate as an example, some people think that the encapsulation of navigation only stays in the formal layer, but it also has encapsulation in function. For example, if the incoming path is a relative path, it will switch according to the current route. Here is the excerpt of useNavigate Code:

export function useNavigate() {  
  let { history, pending } = React.useContext(LocationContext);  
  let { pathname } = React.useContext(RouteContext);  
  
  let navigate = React.useCallback(  
    (to, { replace, state } = {}) => {  
      if (typeof to === "number") {  
        history.go(to);  
      } else {  
        let relativeTo = resolveLocation(to, pathname);  
  
        let method = !!replace || pending ? "replace" : "push";  
        history[method](relativeTo, state);  
      }  
    },  
    [history, pending, pathname]  
  );  
  
  return navigate;  
}

You can see that RouteContext is used to get the current pathname, and to and pathname are spliced according to resolveLocation, and pathname is provided through RouteContext.Provider.

Using multi-layer Context Provider skillfully

Many times, we use Context to stay at the level of one Provider and multiple usecontexts, which is the most basic usage of Context. However, we believe that after reading React Router v6, we can find more usage of Context: multi-layer Context Provider.

Although it is said that there are multiple layers of Context Provider that will adopt the principle of recent coverage, this is not only a function to avoid errors, we can use this function to achieve such improvements as React Router v6.

To illustrate this feature in more detail, here is another specific example: for example, when building a rendering engine, each component has an id, but this id does not show up in the props of the component:

const Input = () => {  
  // The Input component will automatically generate an id in the canvas, but this id component cannot be obtained through props  
};

At this time, if we allow another sub element to be created inside the Input component and want the id of this sub element to be derived from Input, we may need the user to do this:

const Input = ({ id }) => {  
  return <ComponentLoader id={id + "1"} />;  
};

There are two problems in doing so:

  1. Exposing the id to the Input component violates the simplicity of the previous design.
  2. The component needs to assemble the id, which is very troublesome.

The problems encountered here are the same as those encountered by React Router. Can we simplify the code as follows, but the function remains the same?

const Input = () => {  
  return <ComponentLoader id="1" />;  
};

The answer is yes, we can use Context to implement this solution. The key point is to render Input, but the component container needs to wrap a Provider:

const ComponentLoader = ({ id, element }) => {  
  <Context.Provider value={{ id }}>{element}</Context.Provider>;  
};

For internal components, the id obtained by calling useContext at different levels is different, which is exactly the effect we want:

const ComponentLoader = ({id,element}) => {  
  const { id: parentId } = useContext(Context)  
  
  <Context.Provider value={{ id: parentId + id }}>  
    {element}  
  </Context.Provider>  
}

In this way, the < componentloader id = "1" / > we call inside Input actually splices the actual ID 01, which is completely thrown to the external engine layer for processing, and the user does not need to manually splice.

4 Summary

After the React Router v6 is fully refactored with Hooks, it not only simplifies the code, but also becomes more useful. When the official version is released, it can be upgraded quickly.

In addition, from these optimizations made by React Router v6, we have discovered more ingenious use of Context from the source code. I hope this method can help you to apply it to other more complex project designs. Thank you for your reference- http://bjbsair.com/2020-04-01/tech-info/18408.html

Pictures from the Internet

1 Introduction

The version of react route V6 alpha has been released. This week, we will analyze the changes through the article a snake peak at react route V6.

React Router v6: https://github.com/ReactTraining/react-router

A Sneak Peek at React Router v6 : https://alligator.io/react/react-router-v6/

2 Overview

Renamed

A painless change makes API naming more standard.

// v5  
import { BrowserRouter, Switch, Route } from "react-router-dom";  
  
function App() {  
  return (  
    <BrowserRouter>  
      <Switch>  
        <Route exact path="/">  
          <Home />  
        </Route>  
        <Route path="/profile">  
          <Profile />  
        </Route>  
      </Switch>  
    </BrowserRouter>  
  );  
}

In the V6 version of react router, directly use Routes instead of Switch:

// v6  
import { BrowserRouter, Routes, Route } from "react-router-dom";  
  
function App() {  
  return (  
    <BrowserRouter>  
      <Routes>  
        <Route path="/" element={<Home />} />  
        <Route path="profile/*" element={<Profile />} />  
      </Routes>  
    </BrowserRouter>  
  );  
}

upgrade

In v5 version, it is not very intuitive to pass parameters to components. You need to use RenderProps to pass through routeProps:

import Profile from './Profile';  
  
// v5  
<Route path=":userId" component={Profile} />  
<Route  
  path=":userId"  
  render={routeProps => (  
    <Profile {...routeProps} animate={true} />  
  )}  
/>  
  
// v6  
<Route path=":userId" element={<Profile />} />  
<Route path=":userId" element={<Profile animate={true} />} />

In v6, the render and component schemes are combined into element schemes, which can easily pass props without passing through the roteProps parameters.

More convenient nested routing

In v5, nested routes need to get match through useRouteMatch, and sub routes can be realized through match.path splicing:

// v5  
import {  
  BrowserRouter,  
  Switch,  
  Route,  
  Link,  
  useRouteMatch  
} from "react-router-dom";  
  
function App() {  
  return (  
    <BrowserRouter>  
      <Switch>  
        <Route exact path="/" component={Home} />  
        <Route path="/profile" component={Profile} />  
      </Switch>  
    </BrowserRouter>  
  );  
}  
  
function Profile() {  
  let match = useRouteMatch();  
  
  return (  
    <div>  
      <nav>  
        <Link to={`${match.url}/me`}>My Profile</Link>  
      </nav>  
  
      <Switch>  
        <Route path={`${match.path}/me`}>  
          <MyProfile />  
        </Route>  
        <Route path={`${match.path}/:id`}>  
          <OthersProfile />  
        </Route>  
      </Switch>  
    </div>  
  );  
}

In the v6 version, the useRouteMatch step is omitted, and the relative path can be directly represented by path:

// v6  
import { BrowserRouter, Routes, Route, Link, Outlet } from "react-router-dom";  
  
// Approach #1  
function App() {  
  return (  
    <BrowserRouter>  
      <Routes>  
        <Route path="/" element={<Home />} />  
        <Route path="profile/*" element={<Profile />} />  
      </Routes>  
    </BrowserRouter>  
  );  
}  
  
function Profile() {  
  return (  
    <div>  
      <nav>  
        <Link to="me">My Profile</Link>  
      </nav>  
  
      <Routes>  
        <Route path="me" element={<MyProfile />} />  
        <Route path=":id" element={<OthersProfile />} />  
      </Routes>  
    </div>  
  );  
}  
  
// Approach #2  
// You can also define all  
// <Route> in a single place  
function App() {  
  return (  
    <BrowserRouter>  
      <Routes>  
        <Route path="/" element={<Home />} />  
        <Route path="profile" element={<Profile />}>  
          <Route path=":id" element={<MyProfile />} />  
          <Route path="me" element={<OthersProfile />} />  
        </Route>  
      </Routes>  
    </BrowserRouter>  
  );  
}  
  
function Profile() {  
  return (  
    <div>  
      <nav>  
        <Link to="me">My Profile</Link>  
      </nav>  
  
      <Outlet />  
    </div>  
  );  
}

Note that the Outlet is the Element of the render sub route.

useNavigate instead of useHistory

In v5, the active jump route can be used for history.push and other operations through useHistory

// v5  
import { useHistory } from "react-router-dom";  
  
function MyButton() {  
  let history = useHistory();  
  function handleClick() {  
    history.push("/home");  
  }  
  return <button onClick={handleClick}>Submit</button>;  
}

In v6, you can directly implement this common operation through useNavigate:

// v6  
import { useNavigate } from "react-router-dom";  
  
function MyButton() {  
  let navigate = useNavigate();  
  function handleClick() {  
    navigate("/home");  
  }  
  return <button onClick={handleClick}>Submit</button>;  
}

The history is encapsulated in the react router. If history.replace is needed, you can specify it with the {replace: true} parameter:

// v5  
history.push("/home");  
history.replace("/home");  
  
// v6  
navigate("/home");  
navigate("/home", { replace: true });

Smaller volume 8kb

Due to the almost reconstruction of the code, the volume of the v6 version is reduced from 20kb to 8kb after compression.

3 intensive reading

There is a core idea in the source code of react router V6. The author will share it with you, which is very helpful for some framework development. Let's take a look at the code excerpt of useRoutes:

export function useRoutes(routes, basename = "", caseSensitive = false) {  
  let {  
    params: parentParams,  
    pathname: parentPathname,  
    route: parentRoute  
  } = React.useContext(RouteContext);  
  
  if (warnAboutMissingTrailingSplatAt) {  
    // ...  
  }  
  
  basename = basename ? joinPaths([parentPathname, basename]) : parentPathname;  
  
  let navigate = useNavigate();  
  let location = useLocation();  
  let matches = React.useMemo(  
    () => matchRoutes(routes, location, basename, caseSensitive),  
    [routes, location, basename, caseSensitive]  
  );  
  
  // ...  
  
  // Otherwise render an element.  
  let element = matches.reduceRight((outlet, { params, pathname, route }) => {  
    return (  
      <RouteContext.Provider  
        children={route.element}  
        value={{  
          outlet,  
          params: readOnly({ ...parentParams, ...params }),  
          pathname: joinPaths([basename, pathname]),  
          route  
        }}  
      />  
    );  
  }, null);  
  
  return element;  
}

As you can see, with React.Context, the v6 version wraps a layer of RouteContext when rendering each routing element.

For more convenient routing nesting:

In v6 version, useRouteMatch is omitted, and path is directly used to represent relative path.

This is achieved by using this scheme. Because each layer of route file is wrapped with Context, the path of the previous layer can be obtained at each layer. Therefore, when splicing routes, it can be completely implemented by the framework, without the need for users to splice them in advance when invoking.

Taking useNavigate as an example, some people think that the encapsulation of navigation only stays in the formal layer, but it also has encapsulation in function. For example, if the incoming path is a relative path, it will switch according to the current route. Here is the excerpt of useNavigate Code:

export function useNavigate() {  
  let { history, pending } = React.useContext(LocationContext);  
  let { pathname } = React.useContext(RouteContext);  
  
  let navigate = React.useCallback(  
    (to, { replace, state } = {}) => {  
      if (typeof to === "number") {  
        history.go(to);  
      } else {  
        let relativeTo = resolveLocation(to, pathname);  
  
        let method = !!replace || pending ? "replace" : "push";  
        history[method](relativeTo, state);  
      }  
    },  
    [history, pending, pathname]  
  );  
  
  return navigate;  
}

You can see that RouteContext is used to get the current pathname, and to and pathname are spliced according to resolveLocation, and pathname is provided through RouteContext.Provider.

Using multi-layer Context Provider skillfully

Many times, we use Context to stay at the level of one Provider and multiple usecontexts, which is the most basic usage of Context. However, we believe that after reading React Router v6, we can find more usage of Context: multi-layer Context Provider.

Although it is said that there are multiple layers of Context Provider that will adopt the principle of recent coverage, this is not only a function to avoid errors, we can use this function to achieve such improvements as React Router v6.

To illustrate this feature in more detail, here is another specific example: for example, when building a rendering engine, each component has an id, but this id does not show up in the props of the component:

const Input = () => {  
  // The Input component will automatically generate an id in the canvas, but this id component cannot be obtained through props  
};

At this time, if we allow another sub element to be created inside the Input component and want the id of this sub element to be derived from Input, we may need the user to do this:

const Input = ({ id }) => {  
  return <ComponentLoader id={id + "1"} />;  
};

There are two problems in doing so:

  1. Exposing the id to the Input component violates the simplicity of the previous design.
  2. The component needs to assemble the id, which is very troublesome.

The problems encountered here are the same as those encountered by React Router. Can we simplify the code as follows, but the function remains the same?

const Input = () => {  
  return <ComponentLoader id="1" />;  
};

The answer is yes, we can use Context to implement this solution. The key point is to render Input, but the component container needs to wrap a Provider:

const ComponentLoader = ({ id, element }) => {  
  <Context.Provider value={{ id }}>{element}</Context.Provider>;  
};

For internal components, the id obtained by calling useContext at different levels is different, which is exactly the effect we want:

const ComponentLoader = ({id,element}) => {  
  const { id: parentId } = useContext(Context)  
  
  <Context.Provider value={{ id: parentId + id }}>  
    {element}  
  </Context.Provider>  
}

In this way, the < componentloader id = "1" / > we call inside Input actually splices the actual ID 01, which is completely thrown to the external engine layer for processing, and the user does not need to manually splice.

4 Summary

After the React Router v6 is fully refactored with Hooks, it not only simplifies the code, but also becomes more useful. When the official version is released, it can be upgraded quickly.

In addition, from these optimizations made by React Router v6, we have discovered more ingenious use of Context from the source code. I hope this method can help you to apply it to other more complex project designs. Thank you for your reference- http://bjbsair.com/2020-04-01/tech-info/18408.html

Pictures from the Internet

1 Introduction

The version of react route V6 alpha has been released. This week, we will analyze the changes through the article a snake peak at react route V6.

React Router v6: https://github.com/ReactTraining/react-router

A Sneak Peek at React Router v6 : https://alligator.io/react/react-router-v6/

2 Overview

Renamed

A painless change makes API naming more standard.

// v5  
import { BrowserRouter, Switch, Route } from "react-router-dom";  
  
function App() {  
  return (  
    <BrowserRouter>  
      <Switch>  
        <Route exact path="/">  
          <Home />  
        </Route>  
        <Route path="/profile">  
          <Profile />  
        </Route>  
      </Switch>  
    </BrowserRouter>  
  );  
}

In the V6 version of react router, directly use Routes instead of Switch:

// v6  
import { BrowserRouter, Routes, Route } from "react-router-dom";  
  
function App() {  
  return (  
    <BrowserRouter>  
      <Routes>  
        <Route path="/" element={<Home />} />  
        <Route path="profile/*" element={<Profile />} />  
      </Routes>  
    </BrowserRouter>  
  );  
}

upgrade

In v5 version, it is not very intuitive to pass parameters to components. You need to use RenderProps to pass through routeProps:

import Profile from './Profile';  
  
// v5  
<Route path=":userId" component={Profile} />  
<Route  
  path=":userId"  
  render={routeProps => (  
    <Profile {...routeProps} animate={true} />  
  )}  
/>  
  
// v6  
<Route path=":userId" element={<Profile />} />  
<Route path=":userId" element={<Profile animate={true} />} />

In v6, the render and component schemes are combined into element schemes, which can easily pass props without passing through the roteProps parameters.

More convenient nested routing

In v5, nested routes need to get match through useRouteMatch, and sub routes can be realized through match.path splicing:

// v5  
import {  
  BrowserRouter,  
  Switch,  
  Route,  
  Link,  
  useRouteMatch  
} from "react-router-dom";  
  
function App() {  
  return (  
    <BrowserRouter>  
      <Switch>  
        <Route exact path="/" component={Home} />  
        <Route path="/profile" component={Profile} />  
      </Switch>  
    </BrowserRouter>  
  );  
}  
  
function Profile() {  
  let match = useRouteMatch();  
  
  return (  
    <div>  
      <nav>  
        <Link to={`${match.url}/me`}>My Profile</Link>  
      </nav>  
  
      <Switch>  
        <Route path={`${match.path}/me`}>  
          <MyProfile />  
        </Route>  
        <Route path={`${match.path}/:id`}>  
          <OthersProfile />  
        </Route>  
      </Switch>  
    </div>  
  );  
}

In the v6 version, the useRouteMatch step is omitted, and the relative path can be directly represented by path:

// v6  
import { BrowserRouter, Routes, Route, Link, Outlet } from "react-router-dom";  
  
// Approach #1  
function App() {  
  return (  
    <BrowserRouter>  
      <Routes>  
        <Route path="/" element={<Home />} />  
        <Route path="profile/*" element={<Profile />} />  
      </Routes>  
    </BrowserRouter>  
  );  
}  
  
function Profile() {  
  return (  
    <div>  
      <nav>  
        <Link to="me">My Profile</Link>  
      </nav>  
  
      <Routes>  
        <Route path="me" element={<MyProfile />} />  
        <Route path=":id" element={<OthersProfile />} />  
      </Routes>  
    </div>  
  );  
}  
  
// Approach #2  
// You can also define all  
// <Route> in a single place  
function App() {  
  return (  
    <BrowserRouter>  
      <Routes>  
        <Route path="/" element={<Home />} />  
        <Route path="profile" element={<Profile />}>  
          <Route path=":id" element={<MyProfile />} />  
          <Route path="me" element={<OthersProfile />} />  
        </Route>  
      </Routes>  
    </BrowserRouter>  
  );  
}  
  
function Profile() {  
  return (  
    <div>  
      <nav>  
        <Link to="me">My Profile</Link>  
      </nav>  
  
      <Outlet />  
    </div>  
  );  
}

Note that the Outlet is the Element of the render sub route.

useNavigate instead of useHistory

In v5, the active jump route can be used for history.push and other operations through useHistory

// v5  
import { useHistory } from "react-router-dom";  
  
function MyButton() {  
  let history = useHistory();  
  function handleClick() {  
    history.push("/home");  
  }  
  return <button onClick={handleClick}>Submit</button>;  
}

In v6, you can directly implement this common operation through useNavigate:

// v6  
import { useNavigate } from "react-router-dom";  
  
function MyButton() {  
  let navigate = useNavigate();  
  function handleClick() {  
    navigate("/home");  
  }  
  return <button onClick={handleClick}>Submit</button>;  
}

The history is encapsulated in the react router. If history.replace is needed, you can specify it with the {replace: true} parameter:

// v5  
history.push("/home");  
history.replace("/home");  
  
// v6  
navigate("/home");  
navigate("/home", { replace: true });

Smaller volume 8kb

Due to the almost reconstruction of the code, the volume of the v6 version is reduced from 20kb to 8kb after compression.

3 intensive reading

There is a core idea in the source code of react router V6. The author will share it with you, which is very helpful for some framework development. Let's take a look at the code excerpt of useRoutes:

export function useRoutes(routes, basename = "", caseSensitive = false) {  
  let {  
    params: parentParams,  
    pathname: parentPathname,  
    route: parentRoute  
  } = React.useContext(RouteContext);  
  
  if (warnAboutMissingTrailingSplatAt) {  
    // ...  
  }  
  
  basename = basename ? joinPaths([parentPathname, basename]) : parentPathname;  
  
  let navigate = useNavigate();  
  let location = useLocation();  
  let matches = React.useMemo(  
    () => matchRoutes(routes, location, basename, caseSensitive),  
    [routes, location, basename, caseSensitive]  
  );  
  
  // ...  
  
  // Otherwise render an element.  
  let element = matches.reduceRight((outlet, { params, pathname, route }) => {  
    return (  
      <RouteContext.Provider  
        children={route.element}  
        value={{  
          outlet,  
          params: readOnly({ ...parentParams, ...params }),  
          pathname: joinPaths([basename, pathname]),  
          route  
        }}  
      />  
    );  
  }, null);  
  
  return element;  
}

As you can see, with React.Context, the v6 version wraps a layer of RouteContext when rendering each routing element.

For more convenient routing nesting:

In v6 version, useRouteMatch is omitted, and path is directly used to represent relative path.

This is achieved by using this scheme. Because each layer of route file is wrapped with Context, the path of the previous layer can be obtained at each layer. Therefore, when splicing routes, it can be completely implemented by the framework, without the need for users to splice them in advance when invoking.

Taking useNavigate as an example, some people think that the encapsulation of navigation only stays in the formal layer, but it also has encapsulation in function. For example, if the incoming path is a relative path, it will switch according to the current route. Here is the excerpt of useNavigate Code:

export function useNavigate() {  
  let { history, pending } = React.useContext(LocationContext);  
  let { pathname } = React.useContext(RouteContext);  
  
  let navigate = React.useCallback(  
    (to, { replace, state } = {}) => {  
      if (typeof to === "number") {  
        history.go(to);  
      } else {  
        let relativeTo = resolveLocation(to, pathname);  
  
        let method = !!replace || pending ? "replace" : "push";  
        history[method](relativeTo, state);  
      }  
    },  
    [history, pending, pathname]  
  );  
  
  return navigate;  
}

You can see that RouteContext is used to get the current pathname, and to and pathname are spliced according to resolveLocation, and pathname is provided through RouteContext.Provider.

Using multi-layer Context Provider skillfully

Many times, we use Context to stay at the level of one Provider and multiple usecontexts, which is the most basic usage of Context. However, we believe that after reading React Router v6, we can find more usage of Context: multi-layer Context Provider.

Although it is said that there are multiple layers of Context Provider that will adopt the principle of recent coverage, this is not only a function to avoid errors, we can use this function to achieve such improvements as React Router v6.

To illustrate this feature in more detail, here is another specific example: for example, when building a rendering engine, each component has an id, but this id does not show up in the props of the component:

const Input = () => {  
  // The Input component will automatically generate an id in the canvas, but this id component cannot be obtained through props  
};

At this time, if we allow another sub element to be created inside the Input component and want the id of this sub element to be derived from Input, we may need the user to do this:

const Input = ({ id }) => {  
  return <ComponentLoader id={id + "1"} />;  
};

There are two problems in doing so:

  1. Exposing the id to the Input component violates the simplicity of the previous design.
  2. The component needs to assemble the id, which is very troublesome.

The problems encountered here are the same as those encountered by React Router. Can we simplify the code as follows, but the function remains the same?

const Input = () => {  
  return <ComponentLoader id="1" />;  
};

The answer is yes, we can use Context to implement this solution. The key point is to render Input, but the component container needs to wrap a Provider:

const ComponentLoader = ({ id, element }) => {  
  <Context.Provider value={{ id }}>{element}</Context.Provider>;  
};

For internal components, the id obtained by calling useContext at different levels is different, which is exactly the effect we want:

const ComponentLoader = ({id,element}) => {  
  const { id: parentId } = useContext(Context)  
  
  <Context.Provider value={{ id: parentId + id }}>  
    {element}  
  </Context.Provider>  
}

In this way, the < componentloader id = "1" / > we call inside Input actually splices the actual ID 01, which is completely thrown to the external engine layer for processing, and the user does not need to manually splice.

4 Summary

After the React Router v6 is fully refactored with Hooks, it not only simplifies the code, but also becomes more useful. When the official version is released, it can be upgraded quickly.

In addition, from these optimizations made by React Router v6, we have discovered more ingenious use of Context from the source code. I hope this method can help you to apply it to other more complex project designs. Thank you for your reference- http://bjbsair.com/2020-04-01/tech-info/18408.html

Pictures from the Internet

1 Introduction

The version of react route V6 alpha has been released. This week, we will analyze the changes through the article a snake peak at react route V6.

React Router v6: https://github.com/ReactTraining/react-router

A Sneak Peek at React Router v6 : https://alligator.io/react/react-router-v6/

2 Overview

Renamed

A painless change makes API naming more standard.

// v5  
import { BrowserRouter, Switch, Route } from "react-router-dom";  
  
function App() {  
  return (  
    <BrowserRouter>  
      <Switch>  
        <Route exact path="/">  
          <Home />  
        </Route>  
        <Route path="/profile">  
          <Profile />  
        </Route>  
      </Switch>  
    </BrowserRouter>  
  );  
}

In the V6 version of react router, directly use Routes instead of Switch:

// v6  
import { BrowserRouter, Routes, Route } from "react-router-dom";  
  
function App() {  
  return (  
    <BrowserRouter>  
      <Routes>  
        <Route path="/" element={<Home />} />  
        <Route path="profile/*" element={<Profile />} />  
      </Routes>  
    </BrowserRouter>  
  );  
}

upgrade

In v5 version, it is not very intuitive to pass parameters to components. You need to use RenderProps to pass through routeProps:

import Profile from './Profile';  
  
// v5  
<Route path=":userId" component={Profile} />  
<Route  
  path=":userId"  
  render={routeProps => (  
    <Profile {...routeProps} animate={true} />  
  )}  
/>  
  
// v6  
<Route path=":userId" element={<Profile />} />  
<Route path=":userId" element={<Profile animate={true} />} />

In v6, the render and component schemes are combined into element schemes, which can easily pass props without passing through the roteProps parameters.

More convenient nested routing

In v5, nested routes need to get match through useRouteMatch, and sub routes can be realized through match.path splicing:

// v5  
import {  
  BrowserRouter,  
  Switch,  
  Route,  
  Link,  
  useRouteMatch  
} from "react-router-dom";  
  
function App() {  
  return (  
    <BrowserRouter>  
      <Switch>  
        <Route exact path="/" component={Home} />  
        <Route path="/profile" component={Profile} />  
      </Switch>  
    </BrowserRouter>  
  );  
}  
  
function Profile() {  
  let match = useRouteMatch();  
  
  return (  
    <div>  
      <nav>  
        <Link to={`${match.url}/me`}>My Profile</Link>  
      </nav>  
  
      <Switch>  
        <Route path={`${match.path}/me`}>  
          <MyProfile />  
        </Route>  
        <Route path={`${match.path}/:id`}>  
          <OthersProfile />  
        </Route>  
      </Switch>  
    </div>  
  );  
}

In the v6 version, the useRouteMatch step is omitted, and the relative path can be directly represented by path:

// v6  
import { BrowserRouter, Routes, Route, Link, Outlet } from "react-router-dom";  
  
// Approach #1  
function App() {  
  return (  
    <BrowserRouter>  
      <Routes>  
        <Route path="/" element={<Home />} />  
        <Route path="profile/*" element={<Profile />} />  
      </Routes>  
    </BrowserRouter>  
  );  
}  
  
function Profile() {  
  return (  
    <div>  
      <nav>  
        <Link to="me">My Profile</Link>  
      </nav>  
  
      <Routes>  
        <Route path="me" element={<MyProfile />} />  
        <Route path=":id" element={<OthersProfile />} />  
      </Routes>  
    </div>  
  );  
}  
  
// Approach #2  
// You can also define all  
// <Route> in a single place  
function App() {  
  return (  
    <BrowserRouter>  
      <Routes>  
        <Route path="/" element={<Home />} />  
        <Route path="profile" element={<Profile />}>  
          <Route path=":id" element={<MyProfile />} />  
          <Route path="me" element={<OthersProfile />} />  
        </Route>  
      </Routes>  
    </BrowserRouter>  
  );  
}  
  
function Profile() {  
  return (  
    <div>  
      <nav>  
        <Link to="me">My Profile</Link>  
      </nav>  
  
      <Outlet />  
    </div>  
  );  
}

Note that the Outlet is the Element of the render sub route.

useNavigate instead of useHistory

In v5, the active jump route can be used for history.push and other operations through useHistory

// v5  
import { useHistory } from "react-router-dom";  
  
function MyButton() {  
  let history = useHistory();  
  function handleClick() {  
    history.push("/home");  
  }  
  return <button onClick={handleClick}>Submit</button>;  
}

In v6, you can directly implement this common operation through useNavigate:

// v6  
import { useNavigate } from "react-router-dom";  
  
function MyButton() {  
  let navigate = useNavigate();  
  function handleClick() {  
    navigate("/home");  
  }  
  return <button onClick={handleClick}>Submit</button>;  
}

The history is encapsulated in the react router. If history.replace is needed, you can specify it with the {replace: true} parameter:

// v5  
history.push("/home");  
history.replace("/home");  
  
// v6  
navigate("/home");  
navigate("/home", { replace: true });

Smaller volume 8kb

Due to the almost reconstruction of the code, the volume of the v6 version is reduced from 20kb to 8kb after compression.

3 intensive reading

There is a core idea in the source code of react router V6. The author will share it with you, which is very helpful for some framework development. Let's take a look at the code excerpt of useRoutes:

export function useRoutes(routes, basename = "", caseSensitive = false) {  
  let {  
    params: parentParams,  
    pathname: parentPathname,  
    route: parentRoute  
  } = React.useContext(RouteContext);  
  
  if (warnAboutMissingTrailingSplatAt) {  
    // ...  
  }  
  
  basename = basename ? joinPaths([parentPathname, basename]) : parentPathname;  
  
  let navigate = useNavigate();  
  let location = useLocation();  
  let matches = React.useMemo(  
    () => matchRoutes(routes, location, basename, caseSensitive),  
    [routes, location, basename, caseSensitive]  
  );  
  
  // ...  
  
  // Otherwise render an element.  
  let element = matches.reduceRight((outlet, { params, pathname, route }) => {  
    return (  
      <RouteContext.Provider  
        children={route.element}  
        value={{  
          outlet,  
          params: readOnly({ ...parentParams, ...params }),  
          pathname: joinPaths([basename, pathname]),  
          route  
        }}  
      />  
    );  
  }, null);  
  
  return element;  
}

As you can see, with React.Context, the v6 version wraps a layer of RouteContext when rendering each routing element.

For more convenient routing nesting:

In v6 version, useRouteMatch is omitted, and path is directly used to represent relative path.

This is achieved by using this scheme. Because each layer of route file is wrapped with Context, the path of the previous layer can be obtained at each layer. Therefore, when splicing routes, it can be completely implemented by the framework, without the need for users to splice them in advance when invoking.

Taking useNavigate as an example, some people think that the encapsulation of navigation only stays in the formal layer, but it also has encapsulation in function. For example, if the incoming path is a relative path, it will switch according to the current route. Here is the excerpt of useNavigate Code:

export function useNavigate() {  
  let { history, pending } = React.useContext(LocationContext);  
  let { pathname } = React.useContext(RouteContext);  
  
  let navigate = React.useCallback(  
    (to, { replace, state } = {}) => {  
      if (typeof to === "number") {  
        history.go(to);  
      } else {  
        let relativeTo = resolveLocation(to, pathname);  
  
        let method = !!replace || pending ? "replace" : "push";  
        history[method](relativeTo, state);  
      }  
    },  
    [history, pending, pathname]  
  );  
  
  return navigate;  
}

You can see that RouteContext is used to get the current pathname, and to and pathname are spliced according to resolveLocation, and pathname is provided through RouteContext.Provider.

Using multi-layer Context Provider skillfully

Many times, we use Context to stay at the level of one Provider and multiple usecontexts, which is the most basic usage of Context. However, we believe that after reading React Router v6, we can find more usage of Context: multi-layer Context Provider.

Although it is said that there are multiple layers of Context Provider that will adopt the principle of recent coverage, this is not only a function to avoid errors, we can use this function to achieve such improvements as React Router v6.

To illustrate this feature in more detail, here is another specific example: for example, when building a rendering engine, each component has an id, but this id does not show up in the props of the component:

const Input = () => {  
  // The Input component will automatically generate an id in the canvas, but this id component cannot be obtained through props  
};

At this time, if we allow another sub element to be created inside the Input component and want the id of this sub element to be derived from Input, we may need the user to do this:

const Input = ({ id }) => {  
  return <ComponentLoader id={id + "1"} />;  
};

There are two problems in doing so:

  1. Exposing the id to the Input component violates the simplicity of the previous design.
  2. The component needs to assemble the id, which is very troublesome.

The problems encountered here are the same as those encountered by React Router. Can we simplify the code as follows, but the function remains the same?

const Input = () => {  
  return <ComponentLoader id="1" />;  
};

The answer is yes, we can use Context to implement this solution. The key point is to render Input, but the component container needs to wrap a Provider:

const ComponentLoader = ({ id, element }) => {  
  <Context.Provider value={{ id }}>{element}</Context.Provider>;  
};

For internal components, the id obtained by calling useContext at different levels is different, which is exactly the effect we want:

const ComponentLoader = ({id,element}) => {  
  const { id: parentId } = useContext(Context)  
  
  <Context.Provider value={{ id: parentId + id }}>  
    {element}  
  </Context.Provider>  
}

In this way, the < componentloader id = "1" / > we call inside Input actually splices the actual ID 01, which is completely thrown to the external engine layer for processing, and the user does not need to manually splice.

4 Summary

After the React Router v6 is fully refactored with Hooks, it not only simplifies the code, but also becomes more useful. When the official version is released, it can be upgraded quickly.

In addition, from these optimizations made by React Router v6, we have discovered more ingenious use of Context from the source code. I hope this method can help you to apply it to other more complex project designs. Thank you for your reference- http://bjbsair.com/2020-04-01/tech-info/18408.html

Pictures from the Internet

1 Introduction

The version of react route V6 alpha has been released. This week, we will analyze the changes through the article a snake peak at react route V6.

React Router v6: https://github.com/ReactTraining/react-router

A Sneak Peek at React Router v6 : https://alligator.io/react/react-router-v6/

2 Overview

Renamed

A painless change makes API naming more standard.

// v5  
import { BrowserRouter, Switch, Route } from "react-router-dom";  
  
function App() {  
  return (  
    <BrowserRouter>  
      <Switch>  
        <Route exact path="/">  
          <Home />  
        </Route>  
        <Route path="/profile">  
          <Profile />  
        </Route>  
      </Switch>  
    </BrowserRouter>  
  );  
}

In the V6 version of react router, directly use Routes instead of Switch:

// v6  
import { BrowserRouter, Routes, Route } from "react-router-dom";  
  
function App() {  
  return (  
    <BrowserRouter>  
      <Routes>  
        <Route path="/" element={<Home />} />  
        <Route path="profile/*" element={<Profile />} />  
      </Routes>  
    </BrowserRouter>  
  );  
}

upgrade

In v5 version, it is not very intuitive to pass parameters to components. You need to use RenderProps to pass through routeProps:

import Profile from './Profile';  
  
// v5  
<Route path=":userId" component={Profile} />  
<Route  
  path=":userId"  
  render={routeProps => (  
    <Profile {...routeProps} animate={true} />  
  )}  
/>  
  
// v6  
<Route path=":userId" element={<Profile />} />  
<Route path=":userId" element={<Profile animate={true} />} />

In v6, the render and component schemes are combined into element schemes, which can easily pass props without passing through the roteProps parameters.

More convenient nested routing

In v5, nested routes need to get match through useRouteMatch, and sub routes can be realized through match.path splicing:

// v5  
import {  
  BrowserRouter,  
  Switch,  
  Route,  
  Link,  
  useRouteMatch  
} from "react-router-dom";  
  
function App() {  
  return (  
    <BrowserRouter>  
      <Switch>  
        <Route exact path="/" component={Home} />  
        <Route path="/profile" component={Profile} />  
      </Switch>  
    </BrowserRouter>  
  );  
}  
  
function Profile() {  
  let match = useRouteMatch();  
  
  return (  
    <div>  
      <nav>  
        <Link to={`${match.url}/me`}>My Profile</Link>  
      </nav>  
  
      <Switch>  
        <Route path={`${match.path}/me`}>  
          <MyProfile />  
        </Route>  
        <Route path={`${match.path}/:id`}>  
          <OthersProfile />  
        </Route>  
      </Switch>  
    </div>  
  );  
}

In the v6 version, the useRouteMatch step is omitted, and the relative path can be directly represented by path:

// v6  
import { BrowserRouter, Routes, Route, Link, Outlet } from "react-router-dom";  
  
// Approach #1  
function App() {  
  return (  
    <BrowserRouter>  
      <Routes>  
        <Route path="/" element={<Home />} />  
        <Route path="profile/*" element={<Profile />} />  
      </Routes>  
    </BrowserRouter>  
  );  
}  
  
function Profile() {  
  return (  
    <div>  
      <nav>  
        <Link to="me">My Profile</Link>  
      </nav>  
  
      <Routes>  
        <Route path="me" element={<MyProfile />} />  
        <Route path=":id" element={<OthersProfile />} />  
      </Routes>  
    </div>  
  );  
}  
  
// Approach #2  
// You can also define all  
// <Route> in a single place  
function App() {  
  return (  
    <BrowserRouter>  
      <Routes>  
        <Route path="/" element={<Home />} />  
        <Route path="profile" element={<Profile />}>  
          <Route path=":id" element={<MyProfile />} />  
          <Route path="me" element={<OthersProfile />} />  
        </Route>  
      </Routes>  
    </BrowserRouter>  
  );  
}  
  
function Profile() {  
  return (  
    <div>  
      <nav>  
        <Link to="me">My Profile</Link>  
      </nav>  
  
      <Outlet />  
    </div>  
  );  
}

Note that the Outlet is the Element of the render sub route.

useNavigate instead of useHistory

In v5, the active jump route can be used for history.push and other operations through useHistory

// v5  
import { useHistory } from "react-router-dom";  
  
function MyButton() {  
  let history = useHistory();  
  function handleClick() {  
    history.push("/home");  
  }  
  return <button onClick={handleClick}>Submit</button>;  
}

In v6, you can directly implement this common operation through useNavigate:

// v6  
import { useNavigate } from "react-router-dom";  
  
function MyButton() {  
  let navigate = useNavigate();  
  function handleClick() {  
    navigate("/home");  
  }  
  return <button onClick={handleClick}>Submit</button>;  
}

The history is encapsulated in the react router. If history.replace is needed, you can specify it with the {replace: true} parameter:

// v5  
history.push("/home");  
history.replace("/home");  
  
// v6  
navigate("/home");  
navigate("/home", { replace: true });

Smaller volume 8kb

Due to the almost reconstruction of the code, the volume of the v6 version is reduced from 20kb to 8kb after compression.

3 intensive reading

There is a core idea in the source code of react router V6. The author will share it with you, which is very helpful for some framework development. Let's take a look at the code excerpt of useRoutes:

export function useRoutes(routes, basename = "", caseSensitive = false) {  
  let {  
    params: parentParams,  
    pathname: parentPathname,  
    route: parentRoute  
  } = React.useContext(RouteContext);  
  
  if (warnAboutMissingTrailingSplatAt) {  
    // ...  
  }  
  
  basename = basename ? joinPaths([parentPathname, basename]) : parentPathname;  
  
  let navigate = useNavigate();  
  let location = useLocation();  
  let matches = React.useMemo(  
    () => matchRoutes(routes, location, basename, caseSensitive),  
    [routes, location, basename, caseSensitive]  
  );  
  
  // ...  
  
  // Otherwise render an element.  
  let element = matches.reduceRight((outlet, { params, pathname, route }) => {  
    return (  
      <RouteContext.Provider  
        children={route.element}  
        value={{  
          outlet,  
          params: readOnly({ ...parentParams, ...params }),  
          pathname: joinPaths([basename, pathname]),  
          route  
        }}  
      />  
    );  
  }, null);  
  
  return element;  
}

As you can see, with React.Context, the v6 version wraps a layer of RouteContext when rendering each routing element.

For more convenient routing nesting:

In v6 version, useRouteMatch is omitted, and path is directly used to represent relative path.

This is achieved by using this scheme. Because each layer of route file is wrapped with Context, the path of the previous layer can be obtained at each layer. Therefore, when splicing routes, it can be completely implemented by the framework, without the need for users to splice them in advance when invoking.

Taking useNavigate as an example, some people think that the encapsulation of navigation only stays in the formal layer, but it also has encapsulation in function. For example, if the incoming path is a relative path, it will switch according to the current route. Here is the excerpt of useNavigate Code:

export function useNavigate() {  
  let { history, pending } = React.useContext(LocationContext);  
  let { pathname } = React.useContext(RouteContext);  
  
  let navigate = React.useCallback(  
    (to, { replace, state } = {}) => {  
      if (typeof to === "number") {  
        history.go(to);  
      } else {  
        let relativeTo = resolveLocation(to, pathname);  
  
        let method = !!replace || pending ? "replace" : "push";  
        history[method](relativeTo, state);  
      }  
    },  
    [history, pending, pathname]  
  );  
  
  return navigate;  
}

You can see that RouteContext is used to get the current pathname, and to and pathname are spliced according to resolveLocation, and pathname is provided through RouteContext.Provider.

Using multi-layer Context Provider skillfully

Many times, we use Context to stay at the level of one Provider and multiple usecontexts, which is the most basic usage of Context. However, we believe that after reading React Router v6, we can find more usage of Context: multi-layer Context Provider.

Although it is said that there are multiple layers of Context Provider that will adopt the principle of recent coverage, this is not only a function to avoid errors, we can use this function to achieve such improvements as React Router v6.

To illustrate this feature in more detail, here is another specific example: for example, when building a rendering engine, each component has an id, but this id does not show up in the props of the component:

const Input = () => {  
  // The Input component will automatically generate an id in the canvas, but this id component cannot be obtained through props  
};

At this time, if we allow another sub element to be created inside the Input component and want the id of this sub element to be derived from Input, we may need the user to do this:

const Input = ({ id }) => {  
  return <ComponentLoader id={id + "1"} />;  
};

There are two problems in doing so:

  1. Exposing the id to the Input component violates the simplicity of the previous design.
  2. The component needs to assemble the id, which is very troublesome.

The problems encountered here are the same as those encountered by React Router. Can we simplify the code as follows, but the function remains the same?

const Input = () => {  
  return <ComponentLoader id="1" />;  
};

The answer is yes, we can use Context to implement this solution. The key point is to render Input, but the component container needs to wrap a Provider:

const ComponentLoader = ({ id, element }) => {  
  <Context.Provider value={{ id }}>{element}</Context.Provider>;  
};

For internal components, the id obtained by calling useContext at different levels is different, which is exactly the effect we want:

const ComponentLoader = ({id,element}) => {  
  const { id: parentId } = useContext(Context)  
  
  <Context.Provider value={{ id: parentId + id }}>  
    {element}  
  </Context.Provider>  
}

In this way, the < componentloader id = "1" / > we call inside Input actually splices the actual ID 01, which is completely thrown to the external engine layer for processing, and the user does not need to manually splice.

4 Summary

After the React Router v6 is fully refactored with Hooks, it not only simplifies the code, but also becomes more useful. When the official version is released, it can be upgraded quickly.

In addition, from these optimizations made by React Router v6, we have discovered more ingenious use of Context from the source code. I hope this method can help you to apply it to other more complex project designs.

Posted by pojr on Sat, 04 Apr 2020 03:50:13 -0700