preface
This section describes the core principles of the React-Router. Particularly detailed punctuation is off the table.
Preschool tips
The core of the React-Router is the Route component and the History library developed by the unified author. Let’s take a look at the mysterious world of ßReact-Router.
Already know how to use the skip, directly to the source code analysis to ┗ | ` O ‘| ┛ ao ~ ~
A simple example
Let’s build a simple example. React create-react-app create-react
npx create-react-app my-app
cd my-app
npm start
Copy the code
Install the react – the router – the dom
npm install react-router-dom
Copy the code
React-router and react-router-dom are different from each other.
React-router differs from react-router-dom.
First look at the API provided
import { Switch, Route, Router } from 'react-router';
import { Swtich, Route, BrowserRouter, HashHistory, Link } from 'react-router-dom';
Copy the code
React-router
Provides a core API for routing. Such as Router, Route, Switch, etc., but there is no API for dom operation for Route jump;
React-router-dom
BrowserRouter, Route, Link and other apis can be provided to trigger events to control routes through DOM manipulation.
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.
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 package.json dependency in the react-router-dom has a dependency on the react-router. Therefore, NPM does not need to install the React-router.
- Install react-router-dom directly from NPM and use its API.
Simple modification
👌, everyone 🔥 should have been installed, right? The next step is to simply modify the contents of the scaffolding. Mainly to get to know your opponent, know yourself and know your opponent.
Modify SRC /app.js
import React from 'react';
import {
BrowserRouter as Router,
Switch,
Route,
Link,
} from "react-router-dom";
function Home() {
return (
<>
<h1>Home page</h1>
<Link to="/login">The login</Link>
</>)}function Login() {
return (
<>
<h1>The login page</h1>
<Link to="/">Back to the home page</Link>
</>
);
}
function App() {
return (
<Router>
<Switch>
<Route path="/login" component={Login}/>
<Route path="/" component={Home}/>
</Switch>
</Router>
);
}
export default App;
Copy the code
This completes the simplest example possible.
The core idea of SPA
- Listening to the
URL
The change of the - To change some
context
The value of the - Get the corresponding page component
render
The new page
The source code for
BrowserRouter
From the simple example above, we found that there is an outermost buddy called BrowserRouter. Let’s go straight to his Github source.
Link: github.com/ReactTraini…
import React from "react";
import { Router } from "react-router";
import { createBrowserHistory as createHistory } from "history";
import PropTypes from "prop-types";
import warning from "tiny-warning";
/** * The public API for a
that uses HTML5 history. */
class BrowserRouter extends React.Component {
history = createHistory(this.props);
render() {
return <Router history={this.history} children={this.props.children} />; }}if (__DEV__) {
BrowserRouter.propTypes = {
basename: PropTypes.string,
children: PropTypes.node,
forceRefresh: PropTypes.bool,
getUserConfirmation: PropTypes.func,
keyLength: PropTypes.number
};
BrowserRouter.prototype.componentDidMount = function() {
warning(
!this.props.history,
"<BrowserRouter> ignores the history prop. To use a custom history, " +
"use `import { Router }` instead of `import { BrowserRouter as Router }`."
);
};
}
export default BrowserRouter;
Copy the code
Throw away some judgment. See again.
import React from "react";
import { Router } from "react-router";
import { createBrowserHistory as createHistory } from "history";
class BrowserRouter extends React.Component {
history = createHistory(this.props);
render() {
return <Router history={this.history} children={this.props.children} />; }}Copy the code
He was just a middleman. He was making a difference. (pay them!
BrowserRouter relies on two libraries: History and React-Router. Ok, let’s find out.
react-router
Source link: github.com/ReactTraini…
import HistoryContext from "./HistoryContext.js";
import RouterContext from "./RouterContext.js";
Copy the code
Both of these things are actually very simple, they refer to something called createContext, and the purpose is very simple, so it’s just a normal context being created with a specific name. The source code is as follows. Just a few lines.
// TODO: Replace with React.createContext once we can assume React 16+
import createContext from "mini-create-react-context";
const createNamedContext = name= > {
const context = createContext();
context.displayName = name;
return context;
};
export default createNamedContext;
Copy the code
OK, the point is what we need to focus on now, after we get rid of the context. Let me rearrange this.
Look at the constructors first
The constructor
constructor(props) {
super(props);
this.state = {
location: props.history.location
};
// This is a bit of a hack. We have to start listening for location
// changes here in the constructor in case there are any <Redirect>s
// on the initial render. If there are, they will replace/push when
// they mount and since cDM fires in children before parents, we may
// get a new location before the <Router> is mounted.
// _isMounted Indicates whether the component is loaded successfully
this._isMounted = false;
// The component is not loaded yet, but the location changes are present in the _pendingLocation field
this._pendingLocation = null;
// No staticContext attribute, indicating either HashRouter or BrowserRouter
if(! props.staticContext) {this.unlisten = props.history.listen(location= > {
if (this._isMounted) {
// After the component is loaded, the location method state will change
this.setState({ location });
} else {
this._pendingLocation = location; }}); }}Copy the code
☞_isMounted and _pendingLocation have two values
Yes Whether to mount them is to be determined
A location is maintained internally, and the default value is history passed in from outside, and I find where it was passed in.
It’s actually BrowserRouter that you saw earlier
import React from "react";
import { Router } from "react-router";
import { createBrowserHistory as createHistory } from "history";
class BrowserRouter extends React.Component {
history = createHistory(this.props);
render() {
return <Router history={this.history} children={this.props.children} />; }}Copy the code
As you can see, the default value for history is provided by the history library, which will be covered later.
Take a quick look at the translation of the comments (Google Translate)
This is a bit of a hack. We have to start listening for position changes in the constructor in case there are any in the initial render. If any, they will be replaced/pushed at install time, and since the cDM is triggered in the child before the parent, we may get a new location before the installation.
So what does that mean? Let me just describe it:
Because of the child component rendering before the parent component and the presence of the
, it is possible to listen on history.location during the
componentDidMount life cycle before the listener event is registered. The history.location has been changed several times due to the
, so we need to register the listener event from
constructor
Take it back, go into if and continue.
this.unlisten = props.history.listen(location= > {
if (this._isMounted) {
this.setState({ location });
} else {
this._pendingLocation = location; }});Copy the code
The history listen method is called. You can see from the code that you’re listening for history and doing something about it.
componentWillUnmount
So when does this unlisten run?
componentWillUnmount() {
if (this.unlisten) {
this.unlisten();
this._isMounted = false;
this._pendingLocation = null; }}Copy the code
This is done when the Router component is uninstalled. In other words, the listening for History is disabled.
componentDidUnmount
And then look at conponentDidMount
componentDidMount() {
this._isMounted = true;
if (this._pendingLocation) {
this.setState({ location: this._pendingLocation }); }}Copy the code
Simply change the value of the mount or not, and continue the previous determination in the constructor. Save it if there’s any flash data.
render
Render () {return (<RouterContext.Provider value={{// Render () {return (<RouterContext.Provider value={{// // This.props. History, // this.props. History This.state. location, // path url params isExact match: Router.computeRootMatch(this.state.location.pathname), // Only StaticRouter will pass staticContext. // Both HashRouter and BrowserRouter will be null staticContext: this.props.staticContext }} > <HistoryContext.Provider children={this.props.children || null} value={this.props.history} /> </RouterContext.Provider> ); }Copy the code
conclusion
The Router is a component that stores data. Save to Context, there are some special cases between judgment, such as child component rendering before parent component, and Redirect situation handling. Listening for history is removed during uninstallation.
The child component, acting as a consumer, can modify the page, jump, and fetch these values.
history
createBrowserHistory
What is returned
There’s been a lot of history, but let’s see who it is
Source link: github.com/ReactTraini…
The createBrowserHistory that we used earlier, it’s actually an object that’s returned, and it’s got some methods in it that we use a lot.
let history: BrowserHistory = {
get action() {
return action;
},
get location() {
return location;
},
createHref,
push,
replace,
go,
back() {
go(-1);
},
forward() {
go(1);
},
listen(listener) {
return listeners.push(listener);
},
block(blocker) {
let unblock = blockers.push(blocker);
if (blockers.length === 1) {
window.addEventListener(BeforeUnloadEventType, promptBeforeUnload);
}
return function() {
unblock();
// Remove the beforeunload listener so the document may
// still be salvageable in the pagehide event.
// See https://html.spec.whatwg.org/#unloading-documents
if(! blockers.length) {window.removeEventListener(BeforeUnloadEventType, promptBeforeUnload); }}; }};return history;
}
Copy the code
Most of the essence is here. Discover how familiar companions are! => Push, replace, go, etc. Some of them are actually provided by Window.
There are some things that you can see directly in the code that you don’t want to explain, like forward and back.
go
For example, the go method mentioned above intercepts part of the source code.
let globalHistory = window.history;
function go(delta: number) {
globalHistory.go(delta);
}
Copy the code
createHref
// Return a full URL
export function createPath({
pathname = '/',
search = ' ',
hash = ' '
}: PartialPath) {
return pathname + search + hash;
}
// Return a URL
function createHref(to: To) {
/ / see above
return typeof to === 'string' ? to : createPath(to);
}
Copy the code
push
Push source code, with some of the functions that will be used.
// Simply handle the return value.
function getNextLocation(to: To, state: State = null) :Location {
returnreadOnly<Location>({ ... location, ... (typeof to === 'string' ? parsePath(to) : to),
state,
key: createKey()
});
}
// Make a judgment
function allowTx(action: Action, location: Location, retry: () => void) {
return (
If the length is greater than 0, the function is called and the argument is passed. This Blockers will be explored in more detail later! blockers.length || (blockers.call({ action, location, retry }),false)); }// Get state and URL as the name implies
function getHistoryStateAndUrl(
nextLocation: Location,
index: number
) :HistoryState.string] {
return[{usr: nextLocation.state,
key: nextLocation.key,
idx: index
},
// There is a special introduction above
createHref(nextLocation)
];
}
// Return some information about location
function getIndexAndLocation() :number.Location] {
let { pathname, search, hash } = window.location;
let state = globalHistory.state || {};
return [
state.idx,
readOnly<Location>({
pathname,
search,
hash,
state: state.usr || null.key: state.key || 'default'})]; }// Some of the functions (jumps) inside the listeners are implemented, which are explained in detail later
function applyTx(nextAction: Action) {
action = nextAction;
[index, location] = getIndexAndLocation();
listeners.call({ action, location });
}
function push(to: To, state? : State) {
// Here is an enumeration value
let nextAction = Action.Push;
let nextLocation = getNextLocation(to, state);
// Just as the name implies, just one more time
function retry() {
push(to, state);
}
if (allowTx(nextAction, nextLocation, retry)) {
let [historyState, url] = getHistoryStateAndUrl(nextLocation, index + 1);
// TODO: Support forced reloading
// try... catch because iOS limits us to 100 pushState calls :/
try {
/ / MDN address: https://developer.mozilla.org/zh-CN/docs/Web/API/History/pushState
globalHistory.pushState(historyState, ' ', url);
} catch (error) {
// They are going to lose state here, but there is no real
// way to warn them about it since the page will refresh...
/ / MDN address: https://developer.mozilla.org/zh-CN/docs/Web/API/Location/assign
window.location.assign(url); } applyTx(nextAction); }}Copy the code
I have explained this in great detail in the comments, and each function used has an explanation or official URL.
To summarize: a complete flow of history.push
- call
history.pushState
- Error by
window.location.assign
To deal with
- Error by
- To perform a
listeners
The function inside
Yes, you are right, it is as simple as that, but there are many functions called in it, I cut them out and explained them one by one, so that I can understand each line of code, so it is quite long, in a nutshell, it is so simple.
Focus on listen and the createBrowserHistory being called
replace
The function used in this, in front of the push have been resolved, you can go to the above to find, there is no further details.
function replace(to: To, state? : State) {
let nextAction = Action.Replace;
let nextLocation = getNextLocation(to, state);
function retry() {
replace(to, state);
}
if (allowTx(nextAction, nextLocation, retry)) {
let [historyState, url] = getHistoryStateAndUrl(nextLocation, index);
// TODO: Support forced reloading
globalHistory.replaceState(historyState, ' ', url); applyTx(nextAction); }}Copy the code
listen
The listen returned in history is a function. This function was found in the react-router source code and is used for constructor and uninstallation.
listen(listener) {
return listeners.push(listener);
},
Copy the code
Look carefully at what the listeners are doing.
createEvents
Blockers after this function are also used
let listeners = createEvents<Listener>();
function createEvents<F extends Function> () :Events<F> {
let handlers: F[] = [];
return {
get length() {
return handlers.length;
},
push(fn: F) {
handlers.push(fn);
return function() {
handlers = handlers.filter(handler= >handler ! == fn); }; },call(arg) {
handlers.forEach(fn= >fn && fn(arg)); }}; }Copy the code
This method, as the name suggests, creates events. A handlers array is defined to hold the callback function events to be handled.
It then returns an object.
The push method is to add functions to the Handlers to execute.
Listen () returns the listen() method, which calls listeners.
Finally, the call() method is easier to understand, taking the callbacks in the Handlers and executing them one by one.
To summarize, store the pushed functions and filter them. The subsequent calls are executed in sequence. Length is the number of functions you currently have.
And if you cut back, you’ll see that every time you call this Listen you’re pushing a function into an internal variable, handlers.
block
Like Listeners, they are created using createEvents, so we don’t need to go into details. So let’s talk about where we’re going to use this.
let blockers = createEvents<Blocker>();
Copy the code
block(prompt)
– (function) Prevents navigation (see the history docs)
This is the react-router website.
I’m going to give you a quick overview of what happens when a browser is closed or rolled back by mistake. Details can be seen click in view.
conclusion
At this point, the source code for some of the properties returned by our createBrowserHistory call is at hand. But I don’t know exactly how it works.
Specific core principles
Let’s show you the source code. This is the core principle of history. Don’t be fooled by so many lines of code, many of which we explained in the previous push
const PopStateEventType = 'popstate';
let blockedPopTx: Transition | null = null;
function handlePop() {
/ / if you have
if (blockedPopTx) {
blockers.call(blockedPopTx);
blockedPopTx = null;
} else {
let nextAction = Action.Pop;
let [nextIndex, nextLocation] = getIndexAndLocation();
if (blockers.length) {
if(nextIndex ! =null) {
let delta = index - nextIndex;
if (delta) {
// Revert the POP
blockedPopTx = {
action: nextAction,
location: nextLocation,
retry() {
go(delta * -1); }}; go(delta); }}else {
// Trying to POP to a location with no index. We did not create
// this location, so we can't effectively block the navigation.
warning(
false.// TODO: Write up a doc that explains our blocking strategy in
// detail and link to it here so people can understand better what
// is going on and how to avoid it.
`You are trying to block a POP navigation to a location that was not ` +
`created by the history library. The block will fail silently in ` +
`production, but in general you should do all navigation with the ` +
`history library (instead of using window.history.pushState directly) ` +
`to avoid this situation.`); }}else{ applyTx(nextAction); }}}window.addEventListener(PopStateEventType, handlePop);
Copy the code
Window. AddEventListener (PopStateEventType, handlePop)
MDN address: developer.mozilla.org/zh-CN/docs/…
It’s basically listening for changes in the route and then executing a callback function
createHashHistory
This is generally the same as common routing. I want to highlight some issues here.
For example, hash routing anchor problem, I know a few schemes
scrollIntoView
scrollTop
react-anchor-without-hash
Interested in their own inquiry, this is not within the scope of this discussion.
Specific core principles
As mentioned earlier, the core of the history route is window.addeventListener (PopStateEventType, handlePop);
The core of hash routing is also to monitor the change of routing, but the parameters are different.
window.addEventListener('hashchange'.function(e){
/* Listen for changes */
})
Copy the code
And the change of route. The history routes are history.pushState and history.replaceState.
Hash routes are:
window.location.hash
Get and set the hash value using the window.location.hash property.
Specific words are very similar, concerned about the details of the partner can go to see its source oh.
Core API
We looked at BrowserRouter earlier. Let’s go back and see how much of the example code in the demo remains unsolved:
import React from 'react'; import { BrowserRouter as Router, Switch, Route, Link, } from "react-router-dom"; Function Home() {return (<> <h1> </h1> <Link to="/login"> </Link> </>)} function login () {return (<> <h1> <Link to="/"> </Link> </> } function App() { return ( <Router> <Switch> <Route path="/login" component={Login}/> <Route path="/" component={Home}/> </Switch> </Router> ); } export default App;Copy the code
We have looked at BrowserRouter (Router). There are switches and routes below.
Switch
First on the source code. I took the core part.
class Switch extends React.Component { render() { return ( <RouterContext.Consumer> {context => { invariant(context, "You should not use <Switch> outside a <Router>"); Context. location is used by default, but only if there is a customized location. / / are introduced under the const location = this. Props. The location | | context. The location; let element, match; // We use React.Children.forEach instead of React.Children.toArray().find() // here because toArray adds keys to all child elements and we do not want // to trigger an unmount/remount for two <Route>s that render the same // component at // react.children. ForEach (this.props. Children, If (match == null && react. isValidElement(child)) {element = child; // If you want to use Route, you can also use from to match any component, as long as you have the from attribute under the Switch and it matches the current path. Will be rendered / / the from is specific to the < Redirect > use, later said to const path = child. Props. The path | | child. Props. The from; // Check whether the component matches match = path. matchPath(location.pathname, { ... child.props, path }) : context.match; }}); return match ? React.cloneElement(element, { location, computedMatch: match }) : null; }} </RouterContext.Consumer> ); }}Copy the code
How to use the location function in Switch? What use?
From website
location: object
A location object to be used for matching children elements instead of the current history location (usually the current browser URL).
A location object used to match child elements, rather than the current history location (usually the current browser URL).
matchPath
function matchPath(pathname, options = {}) {
// Normalize the structure
// If options is passed as a string, the default string represents path
// If options is passed as an array, as long as there is a match, it is considered a match
if (typeof options === "string" || Array.isArray(options)) {
options = { path: options };
}
const { path, exact = false, strict = false, sensitive = false } = options;
// Convert to array for judgment
const paths = [].concat(path);
// All are very simple content, the difficulty lies in the reduce, this is very interesting, interested or do not know, go to MDN to learn about it!!
return paths.reduce((matched, path) = > {
if(! path && path ! = ="") return null;
// If there is a match, return it as a match
if (matched) return matched;
// regexp is a regular expression
// keys is the value of the cut key
const { regexp, keys } = compilePath(path, {
end: exact,
strict,
sensitive
});
The exec() method returns an array of results if a matching text is found, and an array of results otherwise
const match = regexp.exec(pathname);
/* Failed to match, return null */
if(! match)return null;
// the url represents the matched portion
const [url, ...values] = match;
// pathName === URL indicates a full 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);
}
Copy the code
The matchPath function is also exported by the react-Router function, which can be used to obtain specified parameters in a URL.
Route
A Route is a component layer used to declare Route mapping to an application.
Route has three rendering methods, of course, only one of which will work if all are configured, priority being Children > Component > render
2\1. \ \ 3.
Each is useful in different situations, and in most cases, component is used.
component
Component represents the React component that will render only if the position matches. Create a new React Element using component (instead of Render or children) Route using React. CreateElement (element, props) from the given component. This means that a component created with Component can get props in the router.
children
As shown in the source code, children has a higher priority than Component, and can be a component or function. Children does not get props for the router.
A very special feature of children is that the children method is executed when the route does not match and children is a function, giving the design great flexibility.
render
Render must be a function, with the lowest priority, that is executed when the match is successful.
exact & strict & sensitive
All three are required for path matching using path-to-regexp.
- Exact: If true, it matches only if the path exactly matches location.pathname.
- Strict: The slash after the pathName of the location is considered when determining whether the location matches the current URL.
- Sensitive: True if the path is case sensitive.
location
The Route element tries to match its path to the current browser URL, but you can also use location to match a location other than the current browser location.
The source code for Route is listed below, with the dev section removed.
import React from "react";
import { isValidElementType } from "react-is";
import PropTypes from "prop-types";
import invariant from "tiny-invariant";
import warning from "tiny-warning";
import RouterContext from "./RouterContext.js";
import matchPath from "./matchPath.js";
/** * The public API for matching a single path and rendering. */
class Route extends React.Component {
render() {
return( <RouterContext.Consumer> {context => { invariant(context, "You should not use <Route> outside a <Router>"); / / as you can see, the user location to cover off the context of the location of the const location = this. Props. The location | | context. The location; // If there is a computedMatch, use computedMatch as the result. // matchPath calls path-to-regexp to determine the match. // Path-to-regexp requires three parameters. If true, only if the path matches location.pathname exactly // strict: if true, only one slash will match location.pathname // sensitive: True if the path is case sensitive, This.props.com putedMatch // <Switch> already computed the match for us: this.props.path ? matchPath(location.pathname, this.props) : context.match; // Match () const props = {... // props () const props = {... context, location, match }; // let {children, component, render} = this. Props; // Preact uses an empty array as children by // default, so use null if that's the case. If (array.isarray (children) &&children. Length === 0) {children = null; } return (// RouterContext updates location, Match <RouterContext.Provider value={props}> {props. If children is a function, execute, otherwise return children directly? Typeof children === "function" : children(props) : children // Component // There is a component. Create a new component? React. CreateElement (component, props) // Without component, render: render // With render, render method? Function: typeof children === "function" // if (props) = // if (props) = // if (props) = // children(props) : null} </RouterContext.Provider> ); }} </RouterContext.Consumer> ); } } export default Route;Copy the code
The Route component updates some of the attributes (location and Match) in the RouterContext according to its own parameters. If the current path matches the configured path path, the component will be rendered by children. The most common is the component mode. Note the difference between each mode.
Prompt
Prompt is used to Prompt route switching. This can be useful in situations where the user modifs data on a page and then leaves, prompting the user to save or not. The Prompt component has two properties:
- Message: Text message used to display prompts.
- When: Passes a Boolean value, equivalent to the label switch. Default is true and invalid when set to false.
The essence of a Prompt isto call context.history.block when true to register a route listener for the whole world. Window. confirm is used by default when the path changes. We can also customize the confirm form by passing getUserConfirmation to BrowserRouter or HashRouter. Will replace window.confirm.
import React from "react"; import PropTypes from "prop-types"; import invariant from "tiny-invariant"; import Lifecycle from "./Lifecycle.js"; import RouterContext from "./RouterContext.js"; /** * The public API for prompting the user before navigating away from a screen. */ function Prompt({ message, when = true }) { return ( <RouterContext.Consumer> {context => { invariant(context, "You should not use <Prompt> outside a <Router>"); if (! when || context.staticContext) return null; // call the history.block method const method = context.history.block; return ( <Lifecycle onMount={self => { self.release = method(message); }} onUpdate={(self, prevProps) => { if (prevProps.message ! == message) { self.release(); self.release = method(message); } }} onUnmount={self => { self.release(); }} message={message} /> ); }} </RouterContext.Consumer> ); } export default Prompt;Copy the code
Redirect
Redirect is not so much a component as a set of methods encapsulated by a component that jumps to a new location by calling the History API during the componentDidMount life cycle. By default, the new location overrides the current location in the history stack.
To indicates the url to be redirected to. To can also be a Location object
When push is true, the redirection pushes the new entry into history rather than replacing the current entry.
If a Redirect has a “FROM” attribute, it is retrieved by the Switch. When the “from” attribute matches the current path, the Switch renders the Redirect component and performs the Redirect.
import React from "react";
import PropTypes from "prop-types";
import { createLocation, locationsAreEqual } from "history";
import invariant from "tiny-invariant";
import Lifecycle from "./Lifecycle.js";
import RouterContext from "./RouterContext.js";
import generatePath from "./generatePath.js";
/** * The public API for navigating programmatically with a component. */
function Redirect({ computedMatch, to, push = false }) {
return (
// RouterContext has everything<RouterContext.Consumer> {context => { invariant(context, "You should not use <Redirect> outside a <Router>"); const { history, staticContext } = context; // Generally speaking, Redirect operations do not require a history, so history.replace const method = push? history.push : history.replace; Const location = createLocation(// computedMatch) const location = createLocation(// computedMatch typeof to === "string" ? generatePath(to, computedMatch.params) : { ... to, pathname: generatePath(to.pathname, computedMatch.params) } : to ); // When rendering in a static context, // staticRouter if (staticContext) {method(location); return null; } return (<Lifecycle onMount={() => {// componentDidMount when method(location), Replace method(location); }} onUpdate={(self, prevProps) => {// componentDidUpdate Method (location) = componentDidMount Const prevLocation = createLocation(prevProps. To); if ( ! locationsAreEqual(prevLocation, { ... location, key: prevLocation.key }) ) { method(location); }} // void to={to} />); }} </RouterContext.Consumer> ); } export default Redirect;Copy the code
Lifecycle does not render any page, Lifecycle only provides onMount, onUpdate, onUnmount.
import React from "react";
class Lifecycle extends React.Component {
componentDidMount() {
if (this.props.onMount) this.props.onMount.call(this.this);
}
componentDidUpdate(prevProps) {
if (this.props.onUpdate) this.props.onUpdate.call(this.this, prevProps);
}
componentWillUnmount() {
if (this.props.onUnmount) this.props.onUnmount.call(this.this);
}
render() {
return null; }}export default Lifecycle;
Copy the code
conclusion
The entire React-Router is led by createBrowserHistory or createHashHistory, which binds to our React component and passes some methods and values that belong to the history library. And, of course, route matching and rendering.
In the history library there is listening for routes, changing routes and so on.
process
Use the history model as a reference.
Modify the url
When the URL changes, the listener window.addEventListener(‘ popState ‘, handlePop) written on the window is triggered.
Our function handlePop is called
Inside the function we setState, change the location to pass the correct value down, and use the Switch to find the matching Route component.
A component rendering is triggered.
Of course, there are methods called history.push, history.repalce, etc., which essentially change the URL and then repeat the steps above