Learn about the React-router

1.1. Know the front-end routes

Routing is actually a term in network engineering: two very important devices in architecting a network are the router and the switch.

Of course, the router is also more and more well-known in our production, because we all use the router in our daily life:

  • In fact, a router primarily maintains a mapping table;
  • The mapping table determines where the data will flow;

The concept of routing was first implemented in back-end routing because the Evolution of the Web has gone through several phases:

  • Back-end routing stage;
  • Front and rear end separation stage;
  • Single page rich Application (SPA);

Phase 1: back-end routing phase

In the early days of web development, entire HTML pages were rendered by servers.

  • The server directly produces and renders the corresponding HTML pages and returns them to the client for display.

But how does a web site handle so many pages?

  • A page has its own URL, or URL.

  • The URL is sent to the server, which matches the URL with the re, and gives it to a Controller for processing.

  • The Controller does all sorts of processing and eventually generates HTML or data that is returned to the front end.

  • This completes an IO operation.

This operation is called back-end routing.

  • When we need to request different path content in the page, to the server for processing, the server render the whole page, and the page back to the customer.

  • In this case, the rendered page does not need to load any SEPARATE JS and CSS, can be directly presented to the browser display, which is also conducive to SEO optimization.

Disadvantages of back-end routing:

  • In one case, modules for the entire page are written and maintained by back-end people.

  • On the other hand, a front-end developer who wants to develop a page needs to write the page code in languages like PHP and Java.

  • And often THE HTML code and data and logic get mixed up, making writing and maintenance very messy.

Stage 2: Front and rear end separation stage

Front-end rendering understanding:

  • The static resources involved in each request will be obtained from the static resource server, including HTML+CSS+JS, and then render the resources back to these requests in the front end;

  • Note that every time a client requests a file, it requests it from the static resource server;

  • You can also see that, unlike the previous back-break, the backend is only responsible for providing the API;

Front and rear end separation stage:

  • With the advent of Ajax, there was the development pattern of front and back end separation;

  • The back end only provides apis to return data. The front end gets the data through Ajax and renders it to the page using JavaScript.

  • The biggest advantage of this approach is the clarity of responsibility at the front and back ends, with the back end focusing on data and the front end on interaction and visualization.

  • And when the mobile end (iOS/Android) appears, the back end does not need to do any processing, still use the previous set of API;

  • At present, many websites still use this mode of development (jQuery development mode);

Stage 3: Single Page Rich Application (SPA)

Single page rich application understanding:

  • The English word for single-page rich application is SPA;
  • The entire Web application actually has only one page, and when the URL changes, no new static resources are requested from the server;
  • Instead, JavaScript listens for URL changes and renders new pages based on them.

How can I apply urls and render pages? The front-end routing

  • Front-end routing maintains the mapping between URLS and rendered pages;
  • Routing allows our frameworks (like Vue, React, Angular) to render different components based on different urls;
  • What we end up seeing on the page is actually rendered component pages;

1.2. Principle of front-end routing

How does front-end routing map urls to content? Listen for URL changes.

The URL hash

  • The HASH of the URL, the anchor point (#), essentially changes the href attribute of window.location;

  • We can change the href by assigning location.hash directly, but the page does not refresh;

<div id="app">
  <a href="#/home">home</a>
  <a href="#/about">about</a>
  <div class="router-view"></div>
</div>

<script>
  // 1. Obtain router-view
  const routerViewEl = document.querySelector(".router-view");

  // 2. Listen on hashchange
  window.addEventListener("hashchange".() = > {
    switch(location.hash) {
      case "#/home":
        routerViewEl.innerHTML = "home";
        break;
      case "#/about":
        routerViewEl.innerHTML = "about";
        break;
      default:
        routerViewEl.innerHTML = "default"; }})</script>
Copy the code

The advantage of hash is that it is more compatible and can run in older versions of IE, but the drawback is that it has a # that doesn’t look like a real path.

It’s History

The History interface is a new addition to HTML5 and has six modes for changing urls without refreshing the page:

  • ReplaceState: replaces the original path.
  • PushState: uses the new path.
  • PopState: rollback of a path.
  • Go: to change the path forward or backward;
  • Forword: change path forward;
  • Back: Changes the path backwards.

Let’s briefly demonstrate a few methods:

<div id="app">
  <a href="/home">home</a>
  <a href="/about">about</a>
  <div class="router-view"></div>
</div>

<script>
  // 1. Obtain router-view
  const routerViewEl = document.querySelector(".router-view");

  // 2. Listen on all a elements
  const aEls = document.getElementsByTagName("a");
  for (let aEl of aEls) {
    aEl.addEventListener("click".(e) = > {
      e.preventDefault();
      const href = aEl.getAttribute("href");
      console.log(href);
      history.pushState({}, "", href); historyChange(); })}// 3. Listen for popState and go operations
  window.addEventListener("popstate", historyChange);
  window.addEventListener("go", historyChange);

  // 4. Perform Settings
  function historyChange() {
    switch(location.pathname) {
      case "/home":
        routerViewEl.innerHTML = "home";
        break;
      case "/about":
        routerViewEl.innerHTML = "about";
        break;
      default:
        routerViewEl.innerHTML = "default"; }}</script>
Copy the code

1.3. The react – the router

The three most popular front-end frameworks have their own routing implementations:

  • Presents the ngRouter

  • The React of ReactRouter

  • Vue Vue – the router

With React Router version 4, routes are no longer managed in a single package:

  • React-router is the core code of the router.
  • React-router-dom is for browsers;
  • React-router-native is for native applications;

The latest version of the React Router is v5:

  • In fact, the V4 and V5 versions are not that different;

Install the react – the router:

  • Installing the react-router-dom will automatically help you install the react-router dependencies.
yarn add react-router-dom
Copy the code

React-router is basically used

2.1. The Router is in basic use

The main API of the React-Router provides us with some components:

  • BrowserRouter or HashRouter
    • The Router includes listening for path changes and passes the corresponding paths to the child components.
    • BrowserRouter uses history mode;
    • HashRouter uses hash mode;
  • The Link and NavLink:
    • A typical jump to a path uses a Link component, which ends up being rendered as an A element;
    • NavLink adds style attributes on top of Link (learn more later);
    • To property: The most important property in Link, used to set the path to jump to;
  • The Route:
    • Route is used to match paths.
    • Path property: used to set the matched path;
    • Component property: Render component after setting match to path;
    • Exact: exact matching. Only when the exact path is matched, the corresponding component will be rendered.

Perform the following drills in the App:

import React, { PureComponent } from 'react';

import { BrowserRouter, Route, Link } from 'react-router-dom';

import Home from './pages/home';
import About from './pages/about';
import Profile from './pages/profile';

export default class App extends PureComponent {
  render() {
    return (
      <BrowserRouter>
        <Link to="/">Home page</Link>
        <Link to="/about">about</Link>
        <Link to="/profile">my</Link>

        <Route exact path="/" component={Home} />
        <Route path="/about" component={About} />
        <Route path="/profile" component={Profile} />
      </BrowserRouter>)}}Copy the code

2.2. NavLink usage

When the path is selected, the corresponding a element turns red

In this case, we will use NavLink instead of Link:

  • ActiveStyle: Active (matching) style;
  • ActiveClassName: class added when active;
  • Exact: Whether the match is accurate.

First demonstrate activeStyle:

<NavLink to="/" activeStyle={{color: "red"}} > home page < / NavLink ><NavLink to="/about" activeStyle={{color: "red}} ">about</NavLink>
<NavLink to="/profile" activeStyle={{color: "red}} ">my</NavLink>
Copy the code

However, we will notice that the first one also turns red when about or Profile is selected:

  • The reason is that/paths also match /about or /profile;
  • In this case, we can add the exact attribute to the first NavLink.
<NavLink exact to="/" activeStyle={{color: "red"}} > home page < / NavLink >Copy the code

Default activeClassName:

  • NavLink actually adds a dynamic active class when the default match is successful.
  • So we can also write styles directly
a.active {
  color: red;
}
Copy the code

Of course, if you’re worried about the class being used elsewhere, you can cascade styles, too

<NavLink exact to="/" activeClassName="link-active"> home page < / NavLink ><NavLink to="/about" activeClassName="link-active">about</NavLink>
<NavLink to="/profile" activeClassName="link-active">my</NavLink>
Copy the code

2.3. Functions of the Switch

Let’s look at the following routing rules:

  • When we match a certain path, we find some problems;
  • For example, if the /about path matches,/:useridIs also matched, and the last NoMatch component is always matched;
<Route exact path="/" component={Home} />
<Route path="/about" component={About} />
<Route path="/profile" component={Profile} />
<Route path="/:userid" component={User}/>
<Route component={NoMatch}/>
Copy the code

The reason? By default, components corresponding to routes whose paths are matched in the React-Router are rendered.

But in practice, we often want to have an exclusive mindset:

  • As soon as the first one matches, the next one should not continue to match;
  • In this case, we can use Switch to wrap all routes.
<Switch>
  <Route exact path="/" component={Home} />
  <Route path="/about" component={About} />
  <Route path="/profile" component={Profile} />
  <Route path="/:userid" component={User} />
  <Route component={NoMatch} />
</Switch>
Copy the code

2.3. Use of Redirect

Redirect Redirect is used to Redirect routes. When this component is present, it performs a Redirect to the corresponding to path:

Here’s an example of how to use this:

  • The User page is displayed.
  • However, there is an isLogin in the User interface to record whether the User is logged in:
    • True: then display the user name;
    • False: Directly redirect to the login page.

The Route corresponding to the Login page is defined in advance in app.js:

<Switch> ... Other routes <Route path="/login" component={Login} />
  <Route component={NoMatch} />
</Switch>
Copy the code

Write the corresponding logic code in user.js:

import React, { PureComponent } from 'react'
import { Redirect } from 'react-router-dom';

export default class User extends PureComponent {
  constructor(props) {
    super(props);

    this.state = {
      isLogin: false}}render() {
    return this.state.isLogin ? (
      <div>
        <h2>User</h2>
        <h2>Username: Coderwhy</h2>
      </div>) :<Redirect to="/login"/>}}Copy the code

React-router advanced use

3.1. Route nesting

In development, there are nested relationships between routes.

Here we assume that the About page has two contents:

  • Item list and message list;
  • Click on different links to jump to different places, display different content;
import React, { PureComponent } from 'react';

import { Route, Switch, Link } from 'react-router-dom';

function AboutProduct(props) {
  return (
    <ul>
      <li>List of Goods 1</li>
      <li>Item List 2</li>
      <li>Item List 3</li>
    </ul>)}function AboutMessage(props) {
  return (
    <ul>
      <li>Message List 1</li>
      <li>Message List 2</li>
      <li>Message List 3</li>
    </ul>)}export default class About extends PureComponent {
  render() {
    return (
      <div>
        <Link to="/about">goods</Link>
        <Link to="/about/message">The message</Link>

        <Switch>
          <Route exact path="/about" component={AboutProduct} />
          <Route path="/about/message" component={AboutMessage} />
        </Switch>
      </div>)}}Copy the code

3.2. Manual Jump

At present, we realize the jump is mainly through Link or NavLink to jump, in fact, we can also jump through JavaScript code.

But there is one prerequisite for jumping through JavaScript code: you must get the History object.

How do I get the history object? One of two ways

  • Method 1: If the component is directly jumped to through the route, you can directly obtain the history, location, and match objects.
  • Method 2: If the component is a normal rendered component, then you cannot directly get the history, location, and match objects.

So what if ordinary components also want to get corresponding object properties?

  • Earlier we looked at higher-order components, where you can add desired attributes to the component;
  • React-router also uses higher-order components to add attributes to our components;

If we want to get the history object in the App component, two conditions must be met:

  • The App component must be wrapped within the Router component;
  • App components are wrapped with withRouter higher-order components;

The index.js code is modified as follows:

ReactDOM.render(
  <BrowserRouter>
    <App />
  </BrowserRouter>.document.getElementById('root'));Copy the code

App.js code is modified as follows:

import { Route, Switch, NavLink, withRouter } from 'react-router-dom'; . Omit the other import codeclass App extends PureComponent {
  render() {
    console.log(this.props.history);

    return (
      <div>. Other code<button onClick={e= >Enclosing pushToProfile ()} > me</button>

        <Switch>
          <Route exact path="/" component={Home} />
          <Route path="/about" component={About} />
          <Route path="/profile" component={Profile} />
          <Route path="/:userid" component={User} />
          <Route component={NoMatch} />
        </Switch>
      </div>)}pushToProfile() {
    this.props.history.push("/profile"); }}export default withRouter(App);
Copy the code

Where does history come from? Is it the same as window.history?

We find that withRouter’s higher-order function comes from react-router-dom:

  • Actually from the react-router package;

WithRouter function:

Where does the history object come from?

  • The context actually comes from the code above;

Where do the values of this context come from?

  • From the value of context.consumer;

Where does this.props. History come from?

  • The value passed in from BrowserRouter or HashRouter when it was created;
  • It is passed to the Router, and the Router’s children can get this value from the context;

Where does createBrowserHistory come from?

The essence of doing a push:

2.5. Pass parameters

There are three ways to pass parameters:

  • Dynamic routing;
  • Search passes parameters;
  • To pass object;

Dynamic routing

The concept of dynamic routing refers to the fact that the path in a route is not fixed:

  • Such as/detailPath corresponds to a component Detail;
  • If we write path when Route matches/detail/:id, then/detail/abc,/detail/123The Route can be matched and displayed.
  • This matching rule is called dynamic routing;

In general, dynamic routing is used to pass parameters for the route.

<div> ... Other Link <NavLink to="/detail/abc123"> details < / NavLink ><Switch>. The other Route<Route path="/detail/:id" component={Detail}/>
    <Route component={NoMatch} />
  </Switch>
</div>
Copy the code

The code for detail.js is as follows:

  • We can get the ID directly from the match object;
  • We don’t use withRouter here, because the Detail itself is a jump through the route;
import React, { PureComponent } from 'react'

export default class Detail extends PureComponent {
  render() {
    console.log(this.props.match.params.id);

    return (
      <div>
        <h2>Detail: {this.props.match.params.id}</h2>
      </div>)}}Copy the code

Search transfer parameter

NavLink writing:

  • We added some query parameters to the jump path;
<NavLink to="/detail2? name=why&age=18"> details2</NavLink>

<Switch>
  <Route path="/detail2" component={Detail2}/>
</Switch>
Copy the code

How do I get this in Detail2?

  • In Detail2, search needs to be obtained in location;
  • Note: This search is not parsed, we need to parse it ourselves;
import React, { PureComponent } from 'react'

export default class Detail2 extends PureComponent {
  render() {
    console.log(this.props.location.search); / /? name=why&age=18

    return (
      <div>
        <h2>Detail2:</h2>
      </div>)}}Copy the code

To passed object

To can be passed directly to an object

<NavLink to={{
    pathname: "/detail2".query: {name: "kobe".age: 30},
    state: {height: 1.98.address: "Los Angeles"},
    search: "? apikey=123"}} > for details2
</NavLink>
Copy the code

Get parameters:

import React, { PureComponent } from 'react'

export default class Detail2 extends PureComponent {
  render() {
    console.log(this.props.location);

    return (
      <div>
        <h2>Detail2:</h2>
      </div>)}}Copy the code

4. The react – the router – config

So far all of our Route definitions are done directly using the Route component and adding attributes.

However, this approach can become very confusing, and we want to centralize all routing configurations in one place:

  • You can use react-router-config to do this.

Install the react – the router – config:

yarn add react-router-config
Copy the code

Router /index.js:

import Home from ".. /pages/home";
import About, { AboutMessage, AboutProduct } from ".. /pages/about";
import Profile from ".. /pages/profile";
import Login from ".. /pages/login";
import User from ".. /pages/user";
import Detail from ".. /pages/detail";
import Detail2 from ".. /pages/detail2";
import NoMatch from ".. /pages/nomatch";

const routes = [
  {
    path: "/".exact: true.component: Home
  },
  {
    path: "/about".component: About,
    routes: [{path: "/about".exact: true.component: AboutProduct
      },
      {
        path: "/about/message".component: AboutMessage
      },
    ]
  },
  {
    path: "/profile".component: Profile
  },
  {
    path: "/login".component: Login
  },
  {
    path: "/user".component: User
  },
  {
    path: "/detail/:id".component: Detail
  },
  {
    path: "/detail2".component: Detail2
  },
  {
    component: NoMatch
  }
];

export default routes;
Copy the code

React-router-config renderRoutes:

{renderRoutes(routes)}

{/* 
       
        
        
        
        
        
        
         
          
        
       
       */}
Copy the code

If you need to route a jump, you need to use the renderRoutes function in the child component:

  • There is an extra component in the route component to jump tothis.props.routeProperties;
  • therouteProperty indicates the current route object to be jumped to. You can obtain it by using this propertyroutes;
export default class About extends PureComponent {
  render() {
    return (
      <div>
        <Link to="/about">goods</Link>
        <Link to="/about/message">The message</Link>

        {renderRoutes(this.props.route.routes)}
      </div>)}}Copy the code

A matchRoutes helper function is also provided in react-router-config:

  • matchRoutes(routes, pathname)Pass in an array of routing objects to get all the matching paths;
const routes = matchRoutes(this.props.route.routes, "/about");
console.log(routes);
Copy the code

Looking at the source code for renderRoutes is also very simple:

Note: All of this content will be published on our official website. We will update our tutorials on Flutter, TypeScript, React, Node, Uniapp, MPvue, data structures and algorithms, etc. We will also update some of our own learning experiences