Simple use of React-Router
Handwritten mini React-Router
Link
React-Router
versus Vue-Router
middle Link
It is essentially a tag.
import {Component} from "react" ;
export default class Link extends Component {
render () {
Const {to, Children, ...} = restProps the this .props;
return < A the href = {} to { ... restProps }> {} Children </A > ;
}
}
Copy code
BrowserRouter
according to
React-Router
In the library,Router
Yesreact-router-dom
withreact-router-native
The public components, followed by the library file directory:BrowserRouter
QuoteRouter
And pass in ahistory
parameter
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 code
Context
Create a global context to facilitate the recording of data and transfer across levels
Import React from 'REACT' ;
const RouterContext = React.createContext();
export default RouterContext;
copy the code
Router
Router
The sub-components need to be based onBrowserRouter
Incominghistory
The parameters in are automatically rendered, inRouter
Need to monitorlocation
The change. And pass inProvider
The parameter needs to be an object, and the child component will be re-rendered 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
}
//Monitor location changes
this .unListen = props.history.listen( location => {
this .setState({location});
});
}
componentWillUnmount () {
if ( this .unListen) {
this .unListen();
}
}
render () {
Const {Children, History} = the this .props;
return < RouterContext.Provider value = {{
History ,
LOCATION: this.state.location ,
match: Router.computeRootMatch ( this.state.location.pathname )
}}>
{children}
</RouterContext.Provider > ;
}
}
Copy code
Route
import React, {Component} from "react" ;
import matchPath from "./matchPath" ;
import RouterContext from "./Context" ;
//Show the corresponding components, where children> component> render
export default class Route extends Component {
render () {
return (
< RouterContext.Consumer >
{(context) => {
const {location} = context;
//computedMatch is passed in by the Switch component, if it exists, it can be used directly to make a single selection
const {path, children, component, render, computedMatch} =
this.props;
//Judge the current interface parameters according to location
const match = computedMatch
? computedMatch
: path
? matchPath(location.pathname, this.props)
: context.match;
const props = {
...context,
match,
};
return (
//First judge whether it matches, if it matches, judge whether there are children, then judge whether there is a component, and finally judge whether there is a render
//If there is no match: judge whether there are children (404), if they exist, use children, if they do not exist, return null
< div >
{/* Reason for adding Provider: The component will read the current context value from the Provider that matches the component tree closest to itself, and the most recent props need to be used in the hook */}
< 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 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;
//Only take one match component for rendering
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 code
Redirect
Import React, the 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 an empty component and execute the function in it to jump
class LifeCycle extends Component () {
componentDidMount () {
if ( this .onMount) {
this .onMount();
}
}
render () {
return null ;
}
}
Copy code
withRouter
withRouter
It is essentially a high-level component that receives a component and will RouteContext
middlecontext
Context as props
Pass in the receiving component together and return to the current receiving component
Import React from 'REACT' ;
Import RouterContext from './Context' ;
const withRouter = Component => props => {
return < RouterContext.Consumer >
{context => {
//Pass the context as a parameter to Component
return <Component {...props} {...context}/>;
}}
</RouterContext.Consumer>
}
export default withRouter;
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 ) {
//RouterContext takes the nearest Provider and needs to wrap a layer of RouterContext.Provider in Route
const match = useContext(RouterContext).match;
return match? match.params: null
}
Copy code
matchPath
Use directly
React-Router
Matching route file in
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];
return memo;
}, {})
};
}, null);
}
export default matchPath;