Implement rendering different components according to different paths
There are two ways to achieve this:
HashRouter: it uses monitoring hash change (there is an event hashchange) to realize routing switching. It is a routing container, rendering sub components, and passing (Context context transfer) loading, history and other routing information to the next level sub components
window.addEventListener('hashchange',()=>{window.location.hash})
BrowserHistory: use H5Api to realize route switching. It is a route container, rendering subcomponent, and passing route information such as load, history to subcomponent
The history object provides an interface for manipulating the browser session history
The principle is to use the window.onpushstate method and window.onpoppstate method. When you add an entry (window.history.pushState()) to the browser address bar and click the forward / backward button of the browser, the routing information will be changed
window.history.pushState(state,title,url) adding a record to the browser history entry will not trigger the event, so you need to rewrite the pushState method and write an onpushstate method to listen to the pushState and then trigger the onpushstate method
!(function(history) { let oldPushState = history.pushState //Caching old pushState methods history.pushState = function(state, title, url) { if (typeof window.onpushstate === 'function') { window.onpushstate(state, title, url) } oldPushState.call(history, state, title, url) }})(window.history)
window.onpopstate=function() {} will only trigger under certain actions of the browser, such as clicking backward and forward buttons (or calling history.back(), history.forward(), history.go() in JavaScript.)
Route: it is a routing rule. When path and browser address bar url match, components will be displayed. Generally, there are two attributes, path and component, and exact is an exact match (exact determines whether to exactly match isExact or not)
There are three ways to render a Route component:
- component is the simplest but cannot be judged logically
- Render is a function that can add logical judgment to render the return value of render after execution
One thing component and render have in common is that they render only when the path matches the address bar path
3.children is also a function, but it will render the return value of children after execution regardless of whether the path matches or not
Implementation:
export default class Route extends React.Component { static contextType = RouterContext render() { let paramsName = [] let { pathname } = this.context.location; let { path="/", component: RouteComponent, exact = false ,render,children} = this.props let regexp = pathToRegExp(path, paramsName, { end: exact }) paramsName = paramsName.map(item => item.name) let matched = pathname.match(regexp) let routeProps={ location:this.context.location, history:this.context.history, }//Routing information if (matched) {//Match up to let [url,...values]=matched; let params=values.reduce((memo,cur,index)=>{ memo[paramsName[index]]=cur return memo },{})//{id:1} let match={ url, params, path, isExact:pathname===url } routeProps.match=match if(RouteComponent){ return <RouteComponent {...routeProps}/> }else if(render){ return render(routeProps) }else if(children){ return children(routeProps) }else{ return null } } else {//No match to if(children){ return children(routeProps) }else{ return null } } } }
The components rendered through Route all have routing information (history, location, match)
Components not rendered by Route can be wrapped with withRoute if they want to get Route information
Link: provides the principle of navigation link using history.push(props.to)
Property to:string
to:object
<li className="list-group-item" key={item.id}> <Link to={{ pathname: `/user/detail/${item.id}`, state: item }}>{item.username} </Link> </li>
Print props in the detail component and take props.location.state to get the value of the current object state - additional status data stored in the location. As soon as the hashRouter is refreshed, it will not become undefined
Realization:
<RouterContext.Consumer> { routerValue => { return <a {...props} onClick={()=>routerValue.history.push(props.to)}>{props.children}</a> } } </RouterContext.Consumer>
Switch: render the first child < route > or < redirect > matching the path (the same meaning as the switch case in js)
Redirect: redirection will navigate to a new location that will overwrite the current entry in the history stack
Property: to:string URL to redirect to
<Redirect to="/home/me" />
To: the location of the object to be redirected, where pathname is the url to be redirected, and state can carry information
<Redirect to={{pathname:'/login',state:{path:path}}}/>
from:string only the url provided by from can be redirected to the url specified by to
<Redirect from='/home' to="/" />
Realization:
export default (props) => { let routerValue = useContext(RouterContext) let pathname = routerValue.location.pathname; if (!props.from || (props.from && props.from === pathname)) { routerValue.history.push(props.to) } return null }
NavLink:
You can have the principle of the exact attribute: when the attribute of to matches the address bar path, add an active class name to the component
Realization:
export default (props) => { let { to, children, exact } = props return <Route exact={exact} path={to} children={(routerProps) => { return <Link className={routerProps.match ? 'active' : ''} to={to}>{children} </Link> } } /> }
withRouter:
The function of the higher-order component with Route is that when a custom component is not rendered through the Route component, but you want to obtain the location, history, math and other attributes, you can use the package with Route to obtain the location, history, match and other attributes
Realization:
export default (OldComp)=>{ return props=>{ return <Route render={routerProps=>(<OldComp {...props} {...routerProps}/>)}/> } }