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