Take a look at the react-Router example

const BasicExample = () => (
  <Router>
      <Route exact path="/" component={Home} />
      <Route path="/about" component={About} />
      <Route path="/topics" component={Topics} />
    </div>
  </Router>
);
Copy the code

Click here to see the above code in action

The purpose of this article is to show how the React-Router renders different components based on the URL in the browser. For details on how urls change, see the next article on React-Router: Link hops.

The base depends on path-to-regexp

React to the router provides specialized routing matching method matchPath (located in the router/modules/packages/react matchPath. Js), rely on behind this method is the path – to – the regexp package.

The path-to-regexp input is a path string (that is, the value of path defined in Route), and the output contains two parts

  • Regular expressions (RE)
  • An array of keys (used to record param keys)

Path-to-regexp is defined as a capture group for the parameter in PATH (bar in the following example), and the name of the parameter (bar) is recorded in the array keys

var pathToRegexp = require('path-to-regexp')
var keys = []
var re = pathToRegexp('/foo/:bar', keys) console.log(re); console.log(keys); / / output / ^ \ / foo \ / (/ [^ \] +? (? : \ /)? $/i [ { name:'bar',
    prefix: '/',
    delimiter: '/',
    optional: false,
    repeat: false,
    partial: false,
    pattern: '[^ \ \ /] +? '}]Copy the code

MatchPath core

The matchPath method first obtains the regex corresponding to the path defined on the Route through path-to-regexp method, and then performs regular matching between the generated regular expression and pathname in THE URL to determine whether it is matched.

console.log(re.exec('/foo/randal'));   
console.log(re.exec('/foos/randal')); / / output ['/foo/randal'.'randal', index: 0, input: '/foo/randal' ]
null
Copy the code

Param keys can be associated with param keys by traversing the result of the match and the keys array, as shown below:

const match = re.exec('/foo/randal');
const [url, ...values] = match;

const params = keys.reduce((memo, key, index) => {
  memo[key.name] = values[index];
  return memo;
}, {})

console.log(params) // {"bar": "randal"}
Copy the code

Finally, matchPath returns NULL for an unmatched match and an Object for a successful match

return {
    path,    //  /foo/:bar
    url:     //  /foo/randal
    isExact, //  false
    params:  //  {"bar": "randal"}};Copy the code

The Route of rendering

The Route component maintains a state(match) whose value is taken from the execution result of matchPath, as shown below

state = {
    match: this.computeMatch(this.props, this.context.router)
  };
  computeMatch({ computedMatch, location, path, strict, exact, sensitive }, router) {
  	 if (computedMatch) returncomputedMatch; // computedMatch is left to the Switch using const {route} = router; const pathname = (location || route.location).pathname;return matchPath(pathname, { path, strict, exact, sensitive }, route.match);
  }
Copy the code

Route creates the associated Component only when state.match is not null.

The Route association component has multiple forms (render, Component, children). The difference between children and render and Component is that children are executed regardless of match, even if match is null. The children function is also executed, and the reason for the children design will be discussed in an upcoming article on the Link component.

render() { const { match } = this.state; const { children, component, render } = this.props; const props = { match, ... };if (component) return match ? React.createElement(component, props) : null;

    if (render) return match ? render(props) : null;

    if (typeof children === "function") return children(props);

    return null;
  }
Copy the code

The react-router component renders different routes based on the URL, but sometimes using Route alone can cause problems. For example:

<Route path="/about" component={About}/>
<Route path="/:user" component={User}/>
<Route component={NoMatch}/>
Copy the code

If the current url is /about, this will render the About, User, and NoMatch components on the page, but we want to render only the About component.

Switch Path match before

For the above problem, you can wrap the Switch component around it

<Switch>
  <Route path="/about" component={About}/>
  <Route path="/:user" component={User}/>
  <Route component={NoMatch}/>
</Switch>
Copy the code

After the Switch wrap, only the about component will be rendered for /about, and only the User component for /about.

The feature of the Switch component is that only one Route will be selected from the children to render. In order to achieve the purpose of rendering only one Route, the Switch adopts the Route path matching preposition and does not rely on the Render method of Route to render components, but starts the Route path matching in the Switch. Once a matching path is found, it is selected for rendering. The key code for the Switch is as follows

render() {
    const { route } = this.context.router;
    const { children } = this.props;
    const location = this.props.location || route.location;

    letmatch, child; Children. ForEach (children, element => {// Children => {// Children => {// Children => {if(match == null && React.isValidElement(element)) { const { path: pathProp, exact, strict, sensitive, from } = element.props; const path = pathProp || from; child = element; match = matchPath( location.pathname, { path, exact, strict, sensitive }, route.match ); }});return match
      ? React.cloneElement(child, { location, computedMatch: match })
      : null;
  }
Copy the code

The above code passes match, the result of matchPath execution, into the Route with computedMatch as the key. In this way, duplicate matching is avoided and the computeMatch method of Route can be reused directly. See the Route Rendering section earlier for the computeMatch code.

Enter the next section on react-Router Link jumping