Simple use of the React-Router

The official documentation

Write a mini React – the Router

Link

The Link in react-router and Vue-router is essentially an A tag.

import { Component } from "react";

export default class Link extends Component {
  render() {
    const{ to, children, ... restProps } =this.props;
    return <a href={to} {. restProps} >{children}</a>; }}Copy the code

BrowserRouter

According to the react-Router library, the Router is a common component of the react-router-DOM and react-router-native. BrowserRouter references the Router and passes in a history parameter, along with the library’s file directory: BrowserRouter

import { Component } from "react";
import {createBrowserHistory} from "history";
import Router from "./Router";

export default class BrowserRouter extends Component {
  constructor(props) {
    super(props);
    this.history = createBrowserHistory();
  }

  render() {
    const { children } = this.props;
    return (
      <Router history={this.history}>{ children }</Router>); }}Copy the code

Context

Create a global context for recording data and passing it across hierarchies

import React from 'react';

const RouterContext = React.createContext();

export default RouterContext;
Copy the code

Router

The subcomponents under the Router need to automatically render based on the parameters in the history passed in by BrowserRouter, and the Router needs to listen for location changes. And the parameters passed to the Provider need to be objects before the child component is rerendered according to its context

import React, { Component } from "react";
import RouterContext from './Context';

export default class Router extends Component {
  static computeRootMatch(pathname) {
    return {
      path: "/".url: "/".params: {},
      isExact: pathname === "/"
    };
  }

  constructor(props) {
    super(props);
    this.state = {
      location: props.history.location
    }

    // Listen for location changes
    this.unListen = props.history.listen(location= > {
      this.setState({location});
    });
  }

  componentWillUnmount() {
    if (this.unListen) {
      this.unListen(); }}render() {
    const { children, history } = this.props;
    return <RouterContext.Provider value={{
        history.location: this.state.location.match: Router.computeRootMatch(this.state.location.pathname)
      }}>
      {children}
      </RouterContext.Provider>; }}Copy the code

Route

import React, { Component } from "react";
import matchPath from "./matchPath";
import RouterContext from "./Context";

// Display the corresponding component, children > Component > render
export default class Route extends Component {
  render() {
    return (
      <RouterContext.Consumer>{(context) => { const { location } = context; Const {path, children, component, render, computedMatch} = this.props; Const match = computedMatch? computedMatch : path ? matchPath(location.pathname, this.props) : context.match; const props = { ... context, match, }; Return (// check whether children are matched, check whether component is present, check whether render is present) Check whether children (404) exists. If so, use children; if not, return null<div>{/* The reason for adding a Provider: the component reads the current context value from the Provider closest to it in the component tree, and the hook needs to use the closest props */}<RouterContext.Provider value={props}>
                {match
                  ? children
                    ? typeof children === "function"
                      ? children(props)
                      : children
                    : component
                    ? React.createElement(component, props)
                    : render
                    ? render(props)
                    : null
                  : children
                  ? typeof children === "function"
                    ? children(props)
                    : children
                  : null}
              </RouterContext.Provider>
            </div>
          );
        }}
      </RouterContext.Consumer>); }}Copy the code

Switch

import React, { Component } from "react";
import RouterContext from './Context';
import matchPath from "./matchPath";

export default class Switch extends Component {
  render() {
    const { children } = this.props;
    return (
      <RouterContext.Consumer>{ context => { let match, element; const { location } = context; // just take a match component and render it React.Children. ForEach (Children, (child) => { if (match == null && React.isValidElement(child)) { element = child; const { path } = child.props; match = path ? matchPath(location.pathname, path) : context.match; }}); return match ? React.cloneElement(element, { computedMatch: match }) : null; }}</RouterContext.Consumer>); }}Copy the code

Redirect

import React, { Component } from 'react';
import RouterContext from './Context';

export default class Redirect extends Component {
  render () {
    return (<RouterContext.Consumer>
       {context => {
          const {history, push = false} = context;
          const {to} = this.props;
          return (
            <LifeCycle
              onMount={()= > {
                push ? history.push(to) : history.replace(to);
              }}
            />
          );
       }}
      </RouterContext.Consumer>); }}// Create a component that returns nothing and execute the function to jump
class LifeCycle extends Component(a){
  componentDidMount() {
    if(this.onMount) {
      this.onMount(); }}render() {
    return null; }}Copy the code

withRouter

The withRouter is essentially a higher-order component that receives a component, passes the context context in RouteContext as props to the receiving component, and returns the current receiving component

import React from 'react';
import RouterContext from './Context';

const withRouter = Component= > props= > {
  return <RouterContext.Consumer>{context => {// Pass the context context as an argument to the Component return<Component {. props} {. context} / >;
    }}
  </RouterContext.Consumer>
}

export default withRouter;
Copy the code

hooks

import { useContext }  from 'react';
import RouterContext from './Context';

export function useHistory() {
  return useContext(RouterContext).history;
}

export function useRouteMatch() {
  return useContext(RouterContext).match;
}

export function useLocation() {
  return useContext(RouterContext).location;
}

export function useParams(params) {
  // To get the nearest Provider, wrap the routerContext. Provider layer around the Route
  const match = useContext(RouterContext).match;
  return match ? match.params : null
}
Copy the code

matchPath

Use the react-router matching routing file directly

import pathToRegexp from "path-to-regexp";

const cache = {};
const cacheLimit = 10000;
let cacheCount = 0;

function compilePath(path, options) {
  const cacheKey = `${options.end}${options.strict}${options.sensitive}`;
  const pathCache = cache[cacheKey] || (cache[cacheKey] = {});

  if (pathCache[path]) return pathCache[path];

  const keys = [];
  const regexp = pathToRegexp(path, keys, options);
  const result = { regexp, keys };

  if (cacheCount < cacheLimit) {
    pathCache[path] = result;
    cacheCount++;
  }

  return result;
}

/** * Public API for matching a URL pathname to a path. */
function matchPath(pathname, options = {}) {
  if (typeof options === "string" || Array.isArray(options)) {
    options = { path: options };
  }

  const { path, exact = false, strict = false, sensitive = false } = options;

  const paths = [].concat(path);

  return paths.reduce((matched, path) = > {
    if(! path && path ! = ="") return null;
    if (matched) return matched;

    const { regexp, keys } = compilePath(path, {
      end: exact,
      strict,
      sensitive
    });
    const match = regexp.exec(pathname);

    if(! match)return null;

    const [url, ...values] = match;
    const isExact = pathname === url;

    if(exact && ! isExact)return null;

    return {
      path, // the path used to match
      url: path === "/" && url === "" ? "/" : url, // the matched portion of the URL
      isExact, // whether or not we matched exactly
      params: keys.reduce((memo, key, index) = > {
        memo[key.name] = values[index];
        returnmemo; }, {}}; },null);
}

export default matchPath;

Copy the code