1 Functions to be implemented
When we were developing projects with React, it was basically a single page app, so we needed routing. Routing may seem mysterious, but when we briefly simulate its core functionality, we find that it is. This article will introduce the react-router-DOM HashRouter core implementation logic in detail.
The functions implemented in this paper mainly include:
HashRouter
Route
Link
MenuLink
Switch
Redirect
2 Implementation logic
Let’s take a look at what a HashRouter is:
HashRouter
It’s a big container, and it controls how it renders itself, and what does it control by, as you can guess from its name, is thatwindow.location.hash
.- when
HashRouter
It will be on its own when it starts renderingpathname
Attributes with its bellyRoute
thepath
It matches, and if it matches, it rendersRoute
thecomponent
The corresponding component. Link
How do you switch routes? It’s very simple, just throughthis.props.history.push(path)
To change theHashRouter
In thepathname
Property, which in turn drivesThe Route
Re-render, match our route again, and finally achieve the switch of the route.
After introducing the simple logic, let’s take a look at how to implement it, as shown in the figure below:
HashRouter
It’s an inheritanceReact.Component
On this classstate
includinglocation
And to monitor thehash
Change in order to driveRoute
Component re-render, and one morehistory
Property to switch the routing of the page.- The functions to be implemented in this article include
Route
,Link
,MenuLink
,Switch
,Redirect
, includingRoute
Is that the foundation is the core,MenuLink
And some rendering with specific logicRoute
On the basis of. Route
There are three variables that can be received on a component, includingcomponent
,render
,children
, includingrender
,children
Are both functions,render
Is to render elements according to specific logic,children
It’s used for renderingMenuLink
Both of these functions receive the current routeprops
The return value of the function is the element to render.Switch
The logic of the implementation is, returnchildren
withhash
The first child I ever matched.
3 Specific code logic
(1) HashRouter
HashRouter hooks window.loacation.hash to its state and drives the rerendering of the page by changing its state.
import React, {Component} from 'react';
import PropTypes from 'prop-types';
export default class HashRouter extends Component {
constructor() {
super(a);this.state = {
location: {
pathname: window.location.hash.slice(1) | |'/'.// The hash value of the current page
state: {} // The saved state}}; }// Define the variable type of the context
static childContextTypes = {
location: PropTypes.object,
history: PropTypes.object
}
// Define the context variables
getChildContext() {
return {
location: this.state.location,
history: {
push: (path) = > { // Update the window.hash value
if (typeof path === 'object') {
let {pathname, state} = path;
this.setState({
location: {
...this.state.location,
state // {from: '/profile'}}}, () => {window.location.hash = pathname; })}else {
window.location.hash = path;
}
}
}
}
}
render() {
return this.props.children; // Render the page elements
}
componentDidMount() {
window.location.hash = window.location.hash.slice(1) | |'/';
// Listen for window hash changes and drive the page to refresh
window.addEventListener('hashchange', () = > {this.setState({
location: {
...this.state.location,
pathname: window.location.hash.slice(1) | |'/'}}); }}})Copy the code
(2) Route
The core rendering logic of Route is to match its path with the hash of the current page. If the path matches, render the corresponding element. If the path does not match, render nothing.
import React, {Component} from 'react';
import PropTypes from 'prop-types';
import pathToRegexp from 'path-to-regexp'
export default class Route extends Component {
// Define the type of context
static contextTypes = {
location: PropTypes.object,
history: PropTypes.object
}
render() {
// Deconstruct the props passed to Route
let {path, component: Component, render, children} = this.props;
// Deconstruct the properties of the context
let {location, history} = this.context;
let props = {
location,
history
};
// Match the passed path to the current hash
let keys = [];
let regexp = pathToRegexp(path, keys, {end: false});
keys = keys.map(key= > key.name);
let result = location.pathname.match(regexp);
if (result) { // A match
let [url, ...values] = result;
props.match = {
path,
url,
params: keys.reduce((memo, key, index) = > { // Get the matched parameter
memo[key] = values[index];
returnmemo; }, {}};if (Component) { // Normal Route
return <Component {. props} / >; } else if (render) {return render(props); } else if (children) {// MenuLink render return children(props); } else { return null; }} else {// there is no match if (children) {// MenuLink render return children(props); } else { return null; }}}}Copy the code
(3) Redirect
One thing Redirect does is change the state of the HashRouter to drive the rerendering.
import React, {Component} from 'react';
import PropTypes from 'prop-types';
export default class Redirect extends Component {
// Define the Type of context
static contextTypes = {
history: PropTypes.object
}
componentDidMount() {
// Jump to the destination route
this.context.history.push(this.props.to);
}
render() {
return null; }}Copy the code
(4) MenuLink
import React, {Component} from 'react';
import Route from "./Route";
import Link from './Link'
export default ({to, children}) => {
// If a match is found, give the current component an active className
return <Route path={to} children={props= > (
<li className={props.match ? "active" :""} >
<Link to={to}>{children}</Link>
</li>
)
}/>
}
Copy the code
(5) Link
The Link is rendered as an A tag and then given a click event that changes the state of the HashRouter and drives the re-render.
import React, {Component} from 'react';
import PropTypes from 'prop-types';
export default class Link extends Component {
static contextTypes = {
history: PropTypes.object
}
render() {
return (
<a onClick={()= > this.context.history.push(this.props.to)}>{this.props.children}</a>)}}Copy the code
(6) Switch
import React, {Component} from 'react';
import PropTypes from 'prop-types';
import pathToRegexp from 'path-to-regexp';
export default class Switch extends Component {
static contextTypes = {
location: PropTypes.object
}
render() {
let {pathname} = this.context.location;
let children = this.props.children;
for (let i = 0, l = children.length; i < l; i++) {
let child = children[i];
let path = child.props.path;
if (pathToRegexp(path, [], {end: false}).test(pathname)) {
// Return the first matched element
returnchild; }}return null}}Copy the code
I’ll write 4 at the end
Ok, so that’s it. Do you have any idea how HashRouter works? This article just posted part of the code, if you need to see the demo can be manually experience oh.
References:
- react-router