After a look at the source code, no matter what the problem is easy to find the cause, Waka

Router

Install http-server, React-router-dom, and path-to-regexp to convert paths to re

The purpose is to render different components when you click on different paths

implementation

  1. hashrouter
  2. BrowserRouter

hashrouter

At the earliest, the client realized routing by anchor point, realizing switching page without refreshing.

Use this method to listen for hashchange, which comes with the browser.

// Built-in to the browser
    window.addEventListener('hashchange',(event)=>{
        console.log(event);
    })
Copy the code

BrowserRouter

Use the H5 APi to implement the History object, which provides the interface to manipulate the browser painting history, track a set of locations, and save the index week, pointing to the current location

Run path by

Import routes (routing rules) and routers (containers) from react-router-dom

src/index.js

 import React from 'react';
import ReactDOM from 'react-dom';
import {HashRouter as Router,Route,Link,Switch,Redirect,Forward,Back} from './react-router-dom';
import Home from './components/Home';
import User from './components/User';
import Protected from './components/Protected';
import Profile from './components/Profile';
import Login from './components/Login';
import MenuLink from './components/MenuLink';
import NavHeader from './components/NavHeader';
import 'bootstrap/dist/css/bootstrap.css';
import './components/MenuLink.css';
// One path corresponds to one componentReactDOM.render( <Router> <div> <nav> <div className="navbar navbar-inverse"> <div className="container-fluid"> <div> <ul className="nav navbar-nav"> <MenuLink to= "/' exact={true} lable=" /> <MenuLink to='/user' Lable = "user management" / > < MenuLink to = '/ profile' lable = "privacy Settings" / > < / ul > < / div > < / div > < / div > < / nav > < Switch > < the Route exact path = "/"  component={Home}/> <Route path="/user" component={User}/> <Route path="/login" component={Login}/> <Protected path="/profile" component={Profile}/> <Redirect to="/"/> </Switch> </div> </Router> , document.getElementById('root'));Copy the code

Route indicates the two attributes. Path indicates the path. Component indicates the component

The props attribute of the component is

  • history
  • location {hash,pathname,search,state}
  • match {path,url,isExact,params }

These properties are passed from the Router to the contxt (provider, Consumer) (react.6.3) and are created by react.creatContext ()

We’re going to implement the Home User Protected Profile Login MenuLink NavHeader and the React-router-dom HashRouter, The Route, the Link, the Switch, Redirect, Forward, the function of the Back

The Components Components

The components/Home page

import  React , {Component} from 'react';
export default class Home extends Component{
    render(){
        return (
            <div>Home</div>)}}Copy the code

Components /Profile Permissions page

import React,{Component} from 'react';
export default class Profile extends Component{
	render() {
		return (
			<div>Profile</div>)}}Copy the code

Protected Protected route

import React,{Component} from 'react'
import {Route,Redirect} from '.. /react-router-dom';
// rest = {path,exact,,xxxxxx}
// Return route, execute render, return component, or redirect to put object or path in it
export default function ({component: Component,... rest}) {
	return( <Route {... rest} render={props => ( localStorage.getItem('logined')? <Component {... props} />:<Redirect to={{pathname: '/login',state: {from:props.location.pathname}}}/> )}/> ) }Copy the code

Components /User User interface

import React,{Component} from 'react';
import {Link,Route,Back,Forward} from '.. /react-router-dom';
import UserAdd from './UserAdd';
import UserList from './UserList';
import UserDetail from './UserDetail';


export default class User extends Component{
    componentWillMount() {
		console.log('User componentWillMount');
	}
	componentDidMount() {
		console.log('User componentDidMount');
    }
    render() {
        return( <div className="row"> <div className="col-md-2"> <ul className="nav nav-stacked"> <li><Link To = "/ user/add" > add user < / Link > < / li > < li > < Link to = "/ user/list" > list of users < / Link > < / li > < li > < Back / > < / li > < li > < Forward / > < / li > < / ul > </div> <div className="col-md-10"> <Route path="/user/add" component={UserAdd} /> <Route path="/user/list" component={UserList} /> <Route path="/user/detail/:id" component={UserDetail}/> </div> </div> ) } }Copy the code

Components /UserAdd Adds the user interface

Jump function

import  React , {Component} from 'react';
import api from './api';
import {Prompt} from '.. /react-router-dom'
export default class UserAdd extends Component{
    state={
        isBlocking: false
    }

    componentWillUpdate(nextProps, nextState) {
        console.log('Component WILL UPDATE! ');
        console.log(nextProps, nextState);
    }

    componentDidUpdate(prevProps, prevState) {
        console.log('Component DID UPDATE! ');
        console.log(prevProps, prevState);
    }


    handleSubmit=(event) = > { 
        this.setState({isBlocking: false},()=>{
            event.preventDefault();
            let username=this.username.value;
            let email=this.email.value;
            let user={username,email};
            api.createUser(user);
            this.props.history.push('/user/list');                  
        })
    }
    render(){
        // Submit the form to the jump page
        return(<form onSubmit={this.handlesubmit}> <Prompt when ={this.state.isblocking} message={loc => 'Would you like to switch to ${loc.pathname}' } /> <div className="form-group"> <label htmlFor="username" className="control-label"> </label> <input onChange={()=> this.setState({isBlocking:true})} ref={input=>this.username = input} type="text" className="form-control"/> </div> <div ClassName ="form-group"> <label htmlFor="email" className="control-label"> Mailbox </label> <input onChange={()=> this.setState({isBlocking:true})} ref={input=>this.email = input} type="text" className="form-control"/> </div> <div className="form-group"> <input type="submit" className="btn btn-primary"/> </div> </form> ) } }Copy the code

Components /UserList UserList display page

import  React , {Component} from 'react';
import  api  from './api';
import  {Link}  from '.. /react-router-dom';
import 'bootstrap/dist/css/bootstrap.css';
export default class UserList extends Component{
    state={
        users:[]
    }
    componentWillMount(){
        // Read the user
        let users = api.getUsers();
        this.setState({users});
    }
    handleDelete =(id) = >{
        let users = api.delUser(id);
        this.setState({users});
    }
    render(){
        return (
            <table className="table table-bordered">
                <thead>
                    <tr>
                        <th>ID</th>
                        <th>The user name</th>
                        <th>operation</th>
                    </tr>
                </thead>
                <tbody>
                    {
                        this.state.users.map(user => (                     
                            <tr key={user.id}>{/ *<td><Link to={` /user/detail/ ${user.id} `} >{user.id}</Link></td>* /}<td>
                                        <Link to={{pathname:` /user/detail/ ${user.id} `,state:user}} >
                                            {user.id}
                                        </Link>
                                    </td>                                    
                                    <td>{user.username}</td>
                                    <td>
                                        <button 
                                            onClick={()= >This.handledelete (user.id)} className=" BTN BTN -danger"> delete</button>
                                    </td>
                            </tr>))}</tbody>
            </table>)}}Copy the code

The components/menuLink menu

import React,{Component} from 'react'
import {Route,Link} from '.. /react-router-dom';
import './MenuLink.css'
// There are three ways to render the Route. Component render children
// When to use function components, if you do not need state, try to use function components, concise, controllable
export default  ({to,exact=false,label}) => (
	<Route path={to} exact={exact} children={
		({match}) = > <li className={match? 'active':"'} ><Link to={to}>{label}</Link></li>} / >)Copy the code

The components/navHeader navigation

import React,{Component} from 'react'
import {withRouter} from '.. /react-router-dom';
class NavHeader extends Component{
	render() {
		return (
			<div className="navbar-header">
				<a onClick={()= >This.props.history.push('/')} className="navbar-brand"</a>
			</div>)}}//NavHeader is a normal component that has nothing to do with a Route
export default withRouter(NavHeader);
Copy the code

Components /UserDetail User details page

import  React , {Component} from 'react';
import  api  from './api';
export default class UserDetail extends Component{
    state={
        user:{}
    }
    componentDidMount(){
        let user = this.props.location.state.user;  
        console.log(user);
        if(! user){let id = this.props.match.params.id;
            user = api.getUser(id);
        }      
        this.setState({user});
    }
    render(){
        let user = this.state.user;
        return (
            <div>
                <p>ID:{user.id}</p>
                <p>User name: {user. The username}</p>
                <p>E-mail: {user. Email}</p>
            </div>)}}Copy the code

Components /api.js stores the methods used to add users

let userApi = {
    // Get all users
    getUsers(){
        let usersStr = localStorage.getItem('users');
        return usersStr? JSON.parse(usersStr) :[];
    },

    // Create a user
    createUser(user){
        let users = userApi.getUsers();
        user.id = users.length>0? users[users.length- 1].id + 1:1;
        users.push(user);
        localStorage.setItem('users'.JSON.stringify(users));
    },
    getUser(id){
        return userApi.getUsers().find(user= > user.id == id)
    },
    delUser(id){
        let users = userApi.getUsers().filter(user= >user.id ! = id) localStorage.setItem('users'.JSON.stringify(users));
        returnusers; }}export default userApi;
Copy the code

The react – the router – dom source

react-router-dom/index.js

import HashRouter from './HashRouter';
import Route from './Route';
import Link from './Link';
import Switch from './Switch';
import Redirect from './Redirect';
import Forward from './Forward';
import Back from './Back';
import withRoter from './withRoter';
import Prompt from './Prompt';
export {HashRouter,Route,Link,Switch,Redirect,Back,Forward,withRoter,Prompt}

Copy the code

Hashrouter. Js HashRouter is a component that returns a container

import React , {Component} from 'react'; import { Provider } from './context'; // When the address bar anchor changes, All need to reconfigure the export default class HashRouter extends Component {state = {location: {the pathname: window. The location. The hash? window.location.hash.slice(1):'/' } } componentDidMount(){ window.addEventListener('hashchange',()=>{ this.setState({ location:{ ... this.state.location, pathname:window.location.hash? window.location.hash.slice(1):'/' } }) }) } render(){ let that=this; let value = { location:that.state.location, History :{//value will be passed to the Consumer by the Provider, thus using the method defined here push(to){if(tha.block){// make sure that to is an object let OK = window.confirm(that.block(typeof to === 'object' ? to:{pathname:to})); if(! ok){ return; } } // if(that.unblock){ // window.confirm(that.block) // } if(typeof to === 'object'){ let {pathname,state} = to; that.setState({ ... that.state, location:{ ... that.state.location, pathname, state } },()=>{ window.location.hash = pathname; }) }else{ window.location.hash = to; } }, goback(){ window.history.go(-1); }, forward(){ window.history.go(1); }, // popover method block(message){that.block = message; }, unblock(message){ that.block = null; }} return(// Use Provider to pass data, Consumer <Provider value={value}> {/* children = Route */} {this.props. Children} </Provider>)}}Copy the code

react-router-dom/Route.js

import React,{Component} from 'react';
import {Consumer} from './context';
import pathToRegexp from 'path-to-regexp';
// The address bar needs to be reconfigured every time the anchor points change
//pathToRegexp(' path to convert ', parameter, end or not)
export default class Route extends Component{
    render() {
        return (
            <Consumer>{// receive attribute value => {let {location: {pathname}}=value; // /user let {path="/",component: Component,exact=false,render,children}=this.props; let keys=[]; Let regexp=pathToRegexp(path,keys,{end: exact}); let result=pathname.match(regexp); let props={ location: value.location, history:value.history } if(result){ let [,...values]=result; keys=keys.map(key => key.name); let params = keys.reduce((memo,name,index) => { memo[name]=values[index]; return memo; }, {}); //matchv {path,url,isExact,params } let match={ url:pathname, path, params } props.match=match; // Prototype component render children if (component) {return<Component {. props} / >; } else if (render) { return render(props); } else if (children) { return children(props); } else { return null; } } else { if (children) { return children(props); } else { return null; }}}}</Consumer>)}}Copy the code

/Link.js

import  React , {Component} from 'react';
import  {Consumer} from './context';
// The address bar needs to be reconfigured every time the anchor points change
export default class Link extends Component{
    render(){
        return(
            <Consumer>
                {
                    value => {
                        let {history: {push}} = value;
                        return <a onClick={()= >push(this.props.to)}>{this.props.children}</a>}}</Consumer>)}}Copy the code

Switch. Js match optimization

import React,{Component} from 'react';
import {Consumer} from './context';
import pathToRegexp from 'path-to-regexp';
export default class Switch extends Component{
    render(){
        return (
            <Consumer>{ value =>{ let {location:{pathname}} = value; let children = this.props.children; for(let i=0; i<children.length; i++){
                            let child=children[i];
							//pathThe default value of is /exactThe default value isfalse, not an exact match /user/1
							let {path="/".exact=false}=child.props; //:id
                            let reg=pathToRegexp(path,[],{end: exact});
                            if(reg.test(pathname)) {return child; }}return null;
                    }
                }
            </Consumer>)}}Copy the code

Redirect. Js redirection

import  React , {Component} from 'react';
import  {Consumer} from './context';
export default class Redirect extends Component{
    render(){
        return(
            <Consumer>{ value=>{ value.history.push(this.props.to); return null; }}</Consumer>)}}Copy the code

Back.js

import  React , {Component} from 'react';
import {Consumer} from './context';

export default class Back extends Component{
    render(){
        return (
            <Consumer>
                {
                    value => {
                        return <a onClick={()= >Value. The history. The goback ()} > return</a>; }}</Consumer>)}}Copy the code

Forward.js

import  React , {Component} from 'react';
import {Consumer} from './context';

export default class Back extends Component{
    render(){
        return (
            <Consumer>
                {
                    value => {
                        return <a onClick={()= >Value. The history. The forward ()} > forward</a>; }}</Consumer>)}}Copy the code

WithRoter links the Component to the router

import React from 'react';
import { Route } from '.. /react-router-dom';
//
       is an instance and cannot be returned directly
export default (Component) => (a)= > <Route component = {Component} />
Copy the code

Prompt. Js prevents pop-up bars

import React,{Component} from 'react'
import {Consumer} from './context';
export default class Prompt extends Component{
	componentWillUnmount() {
		this.history.unblock();
	}
	render() {
		return (
			<Consumer>{ value => { this.history=value.history; let {when,message}=this.props; if (when) { value.history.block(message); } else { value.history.unblock(); }}}</Consumer>)}}Copy the code

The principle of Consumer and the provider

Components reuse two policies

  1. High order component
  2. Function committee subcomponents
import React,{Component} from 'react'
import ReactDOM from 'react-dom';
class Panel extends Component{
	render() {
		return (
			<div className="panel panel-default">
			<div className="panel-heading">The head</div>
			<div className="panel-body">{this.props. Children (' panel ')}</div>
		</div>
		)
  }
}
ReactDOM.render(<div>
	<Panel>
	{(text) => <div style={{color:'red'}} >{` is ${text} I `}</div>}
	</Panel>
	<Panel>
	{(text) => <div style={{color:'green'}} >{` is ${text} I `}</div>}
</Panel>
</div>.document.querySelector('#root'));

Copy the code

In the same way

import React,{Component} from 'react'
import ReactDOM from 'react-dom';
//let {Provider,Consumer}=React.createContext();
class Provider extends Component{
	render() {
		let value=this.props.value;
		/ / to the consumer
		let children=this.props.children;
		children = children.map(child= > {
		    // Determine the consumer type
			if (child.type.toString().includes('Consumer'))
			//cloneElement can return props for the same attribute, or it can inherit new props
				return React.cloneElement(child,{value});
			else
				return child;
		});
		return <div>{children}</div>; }}class Consumer extends Component{
	render() {
		return this.props.children(this.props.value);
	}
}
ReactDOM.render((
<Provider value={1}>
		<Consumer>
			{
				value => <div>{value}</div>
			}
		</Consumer>
		<div>2</div>
</Provider>

),document.querySelector('#root')); The Great Wall, do not know the message oh ~Copy the code