React Router implementation

React-router base — history.js

History is a third-party JS library that extends the functionality of the HTML5 History object to manage history. It is compatible with different browsers and different environments, and provides three different apis for different environments.

HashHistory: For older browsers, implemented mainly through Hash. BrowserHistory: for older browsers, mainly through H5 History MemoryHistory: mainly through MemoryHistory, mainly for Node environment.

History supports publish/subscribe and automatically triggers the subscribe function when History changes. The History library is an extension of the native History object, making it more feature-rich

// Internal abstract implementation
function createHistory(options={}) {...return {
    listenBefore, // An internal hook mechanism that can perform certain behaviors before location changes, AOP implementation
    listen, // Trigger a callback when location changes
    transitionTo, // Perform location changes
    push, / / change the location
    replace,
    go,
    goBack,
    goForward,
    createKey, // Create a location key that uniquely identifies the location, which is randomly generated
    createPath,
    createHref,
    createLocation, / / create a location}}Copy the code

The focus is on the implementation of createHistory

Design idea:

  1. A wrapper around History enables recognition to match url changes to componet rendering
  2. Refactoring H5 history from different apis such as BrowserRouter
  3. Structure, while registering the history property.
  4. Register the event callback for History in the Router’s componentWillMount.
  5. Calculate the path from Redirect and call ithistory.push/history.replaceUpdate the history information.
  6. According to the matching results calculated in Route, the first page rendering/page update rendering process is carried out.

To maintain the state, store it in the sessionStorage:

/ / createBrowserHistory createHashHistory state of storage
function saveState(key, state) {...window.sessionStorage.setItem(createKey(key), JSON.stringify(state));
}
function readState(key) {... json =window.sessionStorage.getItem(createKey(key));
  return JSON.parse(json);
}
// createMemoryHistory is only in memory, so the operation is relatively simple
const storage = createStateStorage(entries); // storage = {entry.key: entry.state}

function saveState(key, state) {
  storage[key] = state
}
function readState(key) {
  return storage[key]
}
Copy the code

React-router-dom component structure

React-router-dom provides apis such as BrowserRouter, Route, and Link to control routes by triggering events through DOM operations.

  • Link component, which renders an A tag; The BrowserRouter and HashRouter components, the former using pushState and popState events to build routes, and the latter using hash and HashChange events to build routes.
render() {
    const{ replace, to, innerRef, ... props } =this.props; // eslint-disable-line no-unused-vars

    invariant(
      this.context.router,
      "You should not use <Link> outside a <Router>"); invariant(to ! = =undefined.'You must specify the "to" property');

    const { history } = this.context.router;
    const location =
      typeof to === "string"
        ? createLocation(to, null.null, history.location)
        : to;

    const href = history.createHref(location);
    return (
      <a {. props} onClick={this.handleClick} href={href} ref={innerRef} />
    );
  }
Copy the code
  • React-router-dom extends the REact-router API for manipulating dom.
  • Swtich and Route both import and re-export components from the React-Router without any special processing.

The Route to realize

 render() {
    const { match } = this.state; // A Boolean value indicating whether location matches the path of the current Route
    const { children, component, render } = this.props; // Route provides three alternative rendering methods
    const { history, route, staticContext } = this.context.router; // Router the context passed in
    const location = this.props.location || route.location;
    const props = { match, location, history, staticContext };

    if (component) return match ? React.createElement(component, props) : null; / / Component to create

    if (render) return match ? render(props) : null; / / render to create

    if (typeof children === "function") return children(props); // Callback children created

    if(children && ! isEmptyChildren(children))// Common children created
      return React.Children.only(children);

    return null;
  }
Copy the code

The switch to realize

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

    let match, child;
    React.Children.forEach(children, element= > {
      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

Install react-router-dom directly from NPM and use its API.

Reference links:

The implementation principle of the React – Router