“Post on GitHub Blog”

原文 原文 : A Simple React Router v4 Tutorial

React Router V4 is a popular React package that was completely rewritten using React. Previous versions of the React Router version were configured using dummy components and were obscure. Now with the React Router V4, everything is “just components.”

In this tutorial, we will set up a local “sports team” page, we will complete all the basic requirements to set up our website and route, this includes:

  1. chooserouter.
  2. createroutes.
  3. Navigate between routes through links.

code

The installation

React Router is now divided into three packages: React-router, React-router-dom, and React-router-native.

You should not install the React-Router directly. This package provides the core routing components and functions for the React Router app. The other two packages provide context-specific components (browser and react-Native platform). But they also export the react-router module again.

You should choose one of the two packages that suits your development environment. We need to build a website (that runs in a browser), so we’re going to install react-router-dom

npm install --save react-router-dom
Copy the code

Router

When starting a new project, you should decide which router to use. For projects running in a browser, we can select

components.

should be used in projects where the server processes dynamic requests (knowing how to handle arbitrary URIs). The

is used to process static pages (only responding to requests for known files).

In general, using

is preferred, but if the server only processes requests for static pages, then using

can be a sufficient solution.

For our project, we assume that all pages are dynamically generated by the server, so our router component selects

.

History

Each router creates a History object to keep track of the current location [1] and to rerender the page if it changes. Other components provided by the React Router depend on the History object stored on the context, so they must be rendered inside the Router object. A React Router object without a Router ancestor element will not work properly. If you want to learn more about history objects, see this article.

Rendering a<Router>

Router components can only accept one child element. To comply with this limitation, it is convenient to create an

component to render other applications. (Separating applications from the Router is also important for server-side rendering. Because we can quickly reuse

when we switch to

on the server side)

import { BrowserRouter } from 'react-router-dom'
ReactDOM.render((
  <BrowserRouter>
    <App />
  </BrowserRouter>
), document.getElementById('root'))
Copy the code

Now that we have the Router selected, we can start rendering our real application.

<App>

Our application is defined in the

component. To simplify

, we split our application into two parts. The

component contains the navigation links to other pages, and the

component contains the rest of the sections that need to be rendered.

// this component will be rendered by our <___Router>
const App = (a)= > (
  <div>
    <Header />
    <Main />
  </div>
)
Copy the code

Note: You can lay out your app any way you like. Separating routes and navigation makes it easier to understand how the React Router works.

Let’s start with the


component that renders our routing content.

Routes

The

component is a major part of the React Router. If you want to render something anywhere when the path matches, you should create a

element.

Path

A

component needs a string path prop to specify the path the Route needs to match. For example,

<Route path='/roster'/>
// when the pathname is '/', the path does not match
// when the pathname is '/roster' or '/roster/2', the path matches
// If you only want to match '/roster', then you need to use
// the "exact" prop. The following will match '/roster', but not
// '/roster/2'.
<Route exact path='/roster'/>
// You might find yourself adding the exact prop to most routes.
// In the future (i.e. v5), the exac t prop will likely be true by
// default. For more information on that, you can check out this 
// GitHub issue:
// https://github.com/ReactTraining/react-router/issues/4958
Copy the code

**Note: ** When matching routes, React Router only cares about the relative path part, so the following URL

http://www.example.com/my-projects/one?extra=false
Copy the code

The React Router will only try to match /my-projects/one.

Matching path

The React Router uses the path-to-regexp package to determine if the path prop matches the current path. It converts the path string into a regular expression that matches the current path. There are many alternative formats for the path string. You can see the path-to-regexp documentation.

When a route matches a path, a match object with the following properties is passed in as a prop

  • url– Indicates that the current path matches the route
  • path– the routingpath
  • isExactpath === pathname
  • params– One containspathnamepath-to-regexpCaptured object

**Note: ** Currently, the route path must be an absolute path [4].

Create our own route


s can be created anywhere in the router, but generally it makes more sense to render them in the same place. You can use the

component to combine

s.

will iterate over its children element. Then only the first matching Pathname is matched.

For our site, the path we want to match is:

  1. /– home page
  2. /roster– Team list
  3. /roster/:number– Information on players, identified by their jersey numbers
  4. /schedule– The team schedule

To match the path, we need to create a

element with the path prop

<Switch>
  <Route exact path='/' component={Home}/>
  {/* both /roster and /roster/:number begin with /roster */}
  <Route path='/roster' component={Roster}/>
  <Route path='/schedule' component={Schedule}/>
</Switch>
Copy the code

<Route>What will be rendered

Routes can accept three prop to determine what elements to render when paths match, and only one is provided to the

element to define what to render.

  1. <component>– A React component, when a component withcomponentWhen the route of a prop matches, the route will return the component of the component type provided by the propReact.createElementRendering).
  2. render– A method that returns the React element [5], withcomponentSimilarly, it is called when paths match. It is convenient to render and pass parameters inline.
  3. children– A method that returns the React element. Unlike the first two, this approachalwaysWill be rendered regardless of whether the route matches the current path.
<Route path='/page' component={Page} />
const extraProps = { color: 'red' }
<Route path='/page'render={(props) => ( <Page {... props} data={extraProps}/> )}/> <Route path='/page' children={(props) => ( props.match ? <Page {... props}/> : <EmptyPage {... props}/> )}/>Copy the code

In general, we use Component or render, children is not used in many scenarios, and generally it is best not to render anything when the routes don’t match. In our example, we don’t need to pass any parameters to the route, so we use

.

The element rendered by

will have a list of props, a match object, the current location object [6], and a History object (created by the router) [7].

<Main>

Now that we have identified the structure of the routes, we just need to implement them. In our application, we will render

and

in the


component, which will render the HTML elements in the

.

import { Switch, Route } from 'react-router-dom'
const Main = (a)= > (
  <main>
    <Switch>
      <Route exact path='/' component={Home}/>
      <Route path='/roster' component={Roster}/>
      <Route path='/schedule' component={Schedule}/>
    </Switch>
  </main>
)
Copy the code

**Note: ** The home page route has exact prop. This means that the home page will be matched only if the path of the route matches the pathname.

Nesting of routes

The roster profile page’s routing /roster/:number is in the < roster > component and not included in the

. However, as long as the PathName begins with /roster, it will be rendered by the < roster > component.

In the

component we’ll render two paths:

  1. /roster– Only when the paths match exactly/rosterWill be rendered, we need to specify that pathexactParameters.
  2. /roster/:number– This route is captured using a path parameter/rosterThe pathname section that follows.
const Roster = (a)= > (
  <Switch>
    <Route exact path='/roster' component={FullRoster}/>
    <Route path='/roster/:number' component={Player}/>
  </Switch>
)
Copy the code

It is convenient to have routes with the same prefix in the same component, which simplifies the parent component and allows us to render all components with the same prefix in one place.

For example,

can render a title for all routes beginning with/Roster

const Roster = (a)= >( <div> <h2>This is a roster page! </h2> <Switch> <Route exact path='/roster' component={FullRoster}/> <Route path='/roster/:number' component={Player}/> </Switch> </div> )Copy the code

The Path parameter

Sometimes we want to capture multiple parameters in the pathname. For example, in our player profile route, we can capture the player number by adding a path parameter to the path of the route.

The :number section means that what follows /roster/ in pathName will be stored at match.params.number. For example, a pathname for /roster/6 would have generated the following Params object.

{ number: '6' } // note that the captured value is a string
Copy the code

The component uses the props. Match. Params object to determine which Player’s profile should be rendered.

// an API that returns a player object
import PlayerAPI from './PlayerAPI'
const Player = (props) = > {
  const player = PlayerAPI.get(
    parseInt(props.match.params.number, 10))if(! player) {return <div>Sorry, but the player was not found</div>
  }
  return (
    <div>
      <h1>{player.name} (#{player.number})</h1>
      <h2>{player.position}</h2>
    </div>
)
Copy the code

The path parameter is available in the path-to-regexp documentation.

Right next to , there is also a

,

, and

components.

const FullRoster = (a)= > (
  <div>
    <ul>
      {
        PlayerAPI.all().map(p => (
          <li key={p.number}>
            <Link to={` /roster/ ${p.number} `} >{p.name}</Link>
          </li>))}</ul>
  </div>
)
const Schedule = (a)= > (
  <div>
    <ul>
      <li>6/5 @ Evergreens</li>
      <li>6/8 vs Kickers</li>
      <li>6/14 @ United</li>
    </ul>
  </div>
)
const Home = (a)= > (
  <div>
    <h1>Welcome to the Tornadoes Website!</h1>
  </div>
)
Copy the code

Links

Finally, our site needs to navigate between pages, and if we use the TAB to navigate, a whole new page will load. The React Router provides a component to avoid this. When the is clicked, the URL is updated and the page renders the content without loading the entire new page.

import { Link } from 'react-router-dom'
const Header = (a)= > (
  <header>
    <nav>
      <ul>
        <li><Link to='/'>Home</Link></li>
        <li><Link to='/roster'>Roster</Link></li>
        <li><Link to='/schedule'>Schedule</Link></li>
      </ul>
    </nav>
  </header>
)
Copy the code

s uses to prop to determine the target of navigation, which can be either a string or a Location object (containing pathname, search, hash, and state properties). When it’s just a string, it’s converted to a Location object

<Link to={{ pathname: '/roster/7' }}>Player #7</Link>
Copy the code

**Note: ** Currently, the link’s pathname must be an absolute path.

example

Two online examples:

  1. CodeSandbox
  2. CodePen.

The Notes!

[1] locations are objects that contain parameters that describe different parts of a URL

// a basic location object
{ pathname: '/'.search: ' '.hash: ' '.key: 'abc123' state: {} }
Copy the code

[2] There can be a pathless

that matches all paths, making it easy to access objects and methods stored on the context.

[3] When using children Prop, rendering is performed even when paths do not match.

[4] The work of getting

s and s to accept relative paths is not complete. Relative s is more complicated than it looks, because they require the parent’s match object, not the current URL, to work.

[5] This is a basic stateless component. The difference between Component and render is that Component uses React. CreateElement to create an element, and render uses the component as a function. If you want to create an inline function and pass it to Component, render will come much faster than Component.

<Route path='/one' component={One}/>
// React.createElement(props.component)
<Route path='/two' render={() => <Two />}/>
// props.render()
Copy the code

[6] The

and

components can both accept a location prop, which allows them to be matched to a different location, not just their actual location (the current URL).

[7] Props can also pass a prop staticContext, but this is only valid when rendering on the server.