React Router tutorial
Really learning React is a long process.
You will find that it is not a library, nor a framework, but a huge system. If you want to use its power, the entire technology stack must be transformed with it. You have to learn a whole set of solutions, from the back-end to the front-end, all new practices.
For example, React does not use HTML, but JSX. It intends to abandon the DOM and requires developers not to use any DOM methods. It even abandoned SQL and invented a set of query language GraphQL. Of course, you don’t need to use these, React will still run, but it will not be able to exert its maximum power.
Put it this way, as long as you use React, you will find that a reasonable choice is to adopt its entire technology stack.
This article introduces an important part of the React system: the routing library
React-Router
.
It is officially maintained and in fact is the only optional routing library.
It manages URLs to achieve component switching and state changes, and it will almost certainly be used to develop complex applications.
This article is aimed at beginners, try to be concise and easy to understand. Preliminary knowledge is the basic usage of React. You can refer to the “React Introduction Example Tutorial” written by me .
In addition, I did not prepare a sample library, because the official sample library is very good, from shallow to deep, divided into 14 steps, each step has a detailed code explanation. I strongly recommend that you do it again first, and then watch the API explanation below.
([Description] At the time of writing this article, React-router is version 2.x. The content of this article is only suitable for this version and is not compatible with the latest 4.x version. Currently, the official maintains both versions 2.x and 4.x at the same time , So the former can still be used in the project. March 2017)
1. Basic usage
The React Router installation command is as follows.
$ npm install -S react-router
When used, the router
Router
is a component of React.
import { Router } from 'react-router'; render(<Router/>, document.getElementById('app'));
Router
The component itself is just a container, and the real route must be
Route
defined
by the
component.
import { Router, Route, hashHistory } from 'react-router'; render(( <Router history={hashHistory}> <Route path="/" component={App}/> </Router> ), document.getElementById('app'));
In the above code, if the user accesses the root route
/
(for example
http://www.example.com/
), the component
APP
will be loaded
document.getElementById('app')
.
You may also notice that the
Router
component has a parameter
history
whose value
hashHistory
indicates that the switch of the route is determined by the hash change of the URL, that is, the
#
part of the
URL
changes.
For example, when the user visits
http://www.example.com/
, what he will actually see is
http://www.example.com/#/
.
Route
The component defines the corresponding relationship between the URL path and the component.
You can use multiple
Route
components
at the same time
.
<Router history={hashHistory}> <Route path="/" component={App}/> <Route path="/repos" component={Repos}/> <Route path="/about" component={About}/> </Router>
In the above code, the user accesses
/repos
(for example
http://localhost:8080/#/repos
), the loading
Repos
assembly; access
/about
(
http://localhost:8080/#/about
), the loading
About
assembly.
Two, nested routing
Route
Components can also be nested.
<Router history={hashHistory}> <Route path="/" component={App}> <Route path="/repos" component={Repos}/> <Route path="/about" component={About}/> </Route> </Router>
In the above code, when the user visits
/repos
, the
App
component
will be loaded first
, and then the component
will be loaded
inside it
Repos
.
<App> <Repos/> </App>
App
The components should be written as follows.
export default React.createClass({ render() { return <div> {this.props.children} </div> } })
In the above code,
App
the
this.props.children
attributes of the components are sub-components.
The sub-routes can also not be written in the
Router
component, and
Router
the
routes
properties of the
component
can
be passed in separately
.
let routes = <Route path="/" component={App}> <Route path="/repos" component={Repos}/> <Route path="/about" component={About}/> </Route>; <Router routes={routes} history={browserHistory}/>
Three, the path attribute
Route
The
path
attribute of the
component
specifies the matching rule of the route.
This attribute can be omitted. In this case, no matter whether the path matches or not, the specified component will always be loaded.
Please see the example below.
<Route path="inbox" component={Inbox}> <Route path="messages/:id" component={Message} /> </Route>
In the above code, when the user visits
/inbox/messages/:id
, the following components will be loaded.
<Inbox> <Message/> </Inbox>
If omitted, the outer layer
Route
of the
path
parameters, written in the following way.
<Route component={Inbox}> <Route path="inbox/messages/:id" component={Message} /> </Route>
Now
/inbox/messages/:id
when the
user visits
, the component loading is still the same.
<Inbox> <Message/> </Inbox>
Four, wildcard
path
Attributes can use wildcards.
<Route path="/hello/:name"> // 匹配 /hello/michael // 匹配 /hello/ryan <Route path="/hello(/:name)"> // 匹配 /hello // 匹配 /hello/michael // 匹配 /hello/ryan <Route path="/files/*.*"> // 匹配 /files/hello.jpg // 匹配 /files/hello.html <Route path="/files/*"> // 匹配 /files/ // 匹配 /files/a // 匹配 /files/a/b <Route path="/**/*.jpg"> // 匹配 /files/hello.jpg // 匹配 /files/path/to/file.jpg
The rules for wildcards are as follows.
(1)
:paramName
:paramName
A partial match of the URL, until the next/
,?
,#
so far. This path parameter can bethis.props.params.paramName
retrieved through .(2)
()
()
Indicates that this part of the URL is optional.(3)
*
*
Match any character until the next character in the pattern. The matching method is non-greedy mode.(4)
**
**
Match any character until the next/
,?
,#
. The matching method is greedy mode.
path
Attributes can also use relative paths (not
/
starting with). When matching, they will be relative to the path of the parent component. You can refer to the example in the previous section.
If you want to get rid of this rule with nested routing, you can use absolute routing.
The routing matching rules are executed from top to bottom. Once a match is found, there will be no other rules.
<Route path="/comments" ... /> <Route path="/comments" ... />
In the above code, the path
/comments
matches two rules at the same time, and the second rule will not take effect.
You need to be especially careful about this when setting path parameters.
<Router> <Route path="/:userName/:id" component={UserPage}/> <Route path="/about/me" component={About}/> </Router>
In the above code, when the user visits
/about/me
, the second routing rule will not be triggered because it will match
/:userName/:id
this rule.
Therefore, the path with parameters should generally be written at the bottom of the routing rules.
In addition, the query string of the URL
/foo?bar=baz
can be
this.props.location.query.bar
obtained with.
Five, IndexRoute component
Do you think there is a problem with the following example?
<Router> <Route path="/" component={App}> <Route path="accounts" component={Accounts}/> <Route path="statements" component={Statements}/> </Route> </Router>
In the above code, access to the root path
/
will not load any child components.
That is to say, the
App
components are
this.props.children
at this time
undefined
.
Therefore,
{this.props.children || <Home/>}
this way of writing
is usually used
.
At this time,
Home
obviously
Accounts
, and
Statements
siblings, but did not write
Route
in.
IndexRoute
To solve this problem, explicitly specify
Home
the sub-components of the root route, that is, specify the sub-components that are loaded by default.
You can
IndexRoute
think of it as a certain path
index.html
.
<Router> <Route path="/" component={App}> <IndexRoute component={Home}/> <Route path="accounts" component={Accounts}/> <Route path="statements" component={Statements}/> </Route> </Router>
Now, when the user visits
/
, the structure of the loaded component is as follows.
<App> <Home/> </App>
This component structure is very clear: it
App
only contains the common elements of the lower-level components, and its display content is
Home
defined
by the
component.
This facilitates code separation and also facilitates the use of various APIs provided by React Router.
Note that the
IndexRoute
component has no path parameter
path
.
Six, Redirect component
<Redirect>
The component is used for routing jumps, that is, when a user accesses a route, it will automatically jump to another route.
<Route path="inbox" component={Inbox}> {/* 从 /inbox/messages/:id 跳转到 /messages/:id */} <Redirect from="messages/:id" to="/messages/:id" /> </Route>
Visit now
/inbox/messages/5
, it will automatically jump to
/messages/5
.
Seven, IndexRedirect component
IndexRedirect
When a component is used to access the root route, it redirects the user to a certain subcomponent.
<Route path="/" component={App}> <IndexRedirect to="/welcome" /> <Route path="welcome" component={Welcome} /> <Route path="about" component={About} /> </Route>
In the above code, when the user visits the root path, it will be automatically redirected to the subcomponent
welcome
.
8. Link
Link
The component is used to replace the
<a>
element and generate a link that allows the user to click to jump to another route.
It is basically
<a>
the React version of the element and
Router
the state it
can receive
.
render() { return <div> <ul role="nav"> <li><Link to="/about">About</Link></li> <li><Link to="/repos">Repos</Link></li> </ul> </div> }
If you want the current route to have a different style from other routes, you can use
Link
the
activeStyle
properties of the
component at
this time
.
<Link to="/about" activeStyle={{color: 'red'}}>About</Link> <Link to="/repos" activeStyle={{color: 'red'}}>Repos</Link>
In the above code, the link of the current page will be displayed in red.
Another approach is to use the one
activeClassName
that specifies the current route
Class
.
<Link to="/about" activeClassName="active">About</Link> <Link to="/repos" activeClassName="active">Repos</Link>
In the above code, the link of the current page
class
will be included
active
.
Router
Outside of
the
component, to navigate to the routing page, you can use the History API of the browser, and write like the following.
import { browserHistory } from 'react-router'; browserHistory.push('/some/path');
Nine, IndexLink
If you link to the root route
/
, don’t use
Link
components, but use
IndexLink
components.
This is because for the root route, the
activeStyle
sum
activeClassName
will be invalid, or it will always take effect, because it
/
will match any sub-routes.
And the
IndexLink
component will use the exact match of the path.
<IndexLink to="/" activeClassName="active"> Home </IndexLink>
In the above code, the root route will only have an exact match
activeClassName
.
Another method is to use
Link
the
onlyActiveOnIndex
properties of the
component
, which can achieve the same effect.
<Link to="/" activeClassName="active" onlyActiveOnIndex={true}> Home </Link>
In fact, it
IndexLink
is
the packaging
Link
of the
onlyActiveOnIndex
properties of
the
component
.
Ten, histroy attributes
Router
The
history
attribute of the
component is
used to monitor changes in the address bar of the browser and resolve the URL into an address object for React Router to match.
history
Property, there are three values that can be set in total.
- browserHistory
- hashHistory
- createMemoryHistory
If set
hashHistory
, the route will be
#
switched
through the hash part (
) of the URL, and the form of the URL is similar
example.com/#/some/path
.
import { hashHistory } from 'react-router' render( <Router history={hashHistory} routes={routes} />, document.getElementById('app') )
If it is set
browserHistory
, the browser’s routing will no longer be
Hash
completed, but the normal path
will be
displayed, and
example.com/some/path
the history API of the browser will be called behind.
import { browserHistory } from 'react-router' render( <Router history={browserHistory} routes={routes} />, document.getElementById('app') )
However, this situation requires server modification . Otherwise, if the user directly requests a sub-route from the server, a 404 error that the webpage cannot be found will be displayed.
If the development server is used
webpack-dev-server
, just add
--history-api-fallback
parameters.
$ webpack-dev-server --inline --content-base . --history-api-fallback
createMemoryHistory
Mainly used for server rendering.
It creates an
history
object in
memory
and does not interact with the browser URL.
const history = createMemoryHistory(location)
11. Form processing
Link
Components are used for normal users to click to jump, but sometimes operations such as form jumps and button clicks are needed to jump.
How do these situations connect with React Router?
Below is a form.
<form onSubmit={this.handleSubmit}> <input type="text" placeholder="userName"/> <input type="text" placeholder="repo"/> <button type="submit">Go</button> </form>
The first method is to use
browserHistory.push
import { browserHistory } from 'react-router' // ... handleSubmit(event) { event.preventDefault() const userName = event.target.elements[0].value const repo = event.target.elements[1].value const path = `/repos/${userName}/${repo}` browserHistory.push(path) },
The second method is to use
context
objects.
export default React.createClass({ // ask for `router` from context contextTypes: { router: React.PropTypes.object }, handleSubmit(event) { // ... this.context.router.push(path) }, })
12. Routing hook
Each route has
Enter
and
Leave
hooks, triggered when users enter or leave the route.
<Route path="about" component={About} /> <Route path="inbox" component={Inbox}> <Redirect from="messages/:id" to="/messages/:id" /> </Route>
In the above code, if the user leaves
/messages/:id
and enters
/about
, the following hooks will be triggered in turn.
/messages/:id
ofonLeave
/inbox
ofonLeave
/about
ofonEnter
Here is an example, using
onEnter
hooks instead of
<Redirect>
components.
<Route path="inbox" component={Inbox}> <Route path="messages/:id" onEnter={ ({params}, replace) => replace(`/messages/${params.id}`) } /> </Route>
onEnter
Hooks can also be used for authentication.
const requireAuth = (nextState, replace) => { if (!auth.isAdmin()) { // Redirect to Home page if not an Admin replace({ pathname: '/' }) } } export const AdminRoutes = () => { return ( <Route path="/admin" component={Admin} onEnter={requireAuth} /> ) }
The following is an advanced application. When the user leaves a path, a prompt box will pop up, asking the user to confirm whether to leave.
const Home = withRouter( React.createClass({ componentDidMount() { this.props.router.setRouteLeaveHook( this.props.route, this.routerWillLeave ) }, routerWillLeave(nextLocation) { // 返回 false 会继续停留当前页面, // 否则,返回一个字符串,会显示给用户,让其自己决定 if (!this.state.isSaved) return '确认要离开?'; }, }) )
In the above code, the
setRouteLeaveHook
method
Leave
specifies the
routerWillLeave
function
for the
hook
.
If this method returns
false
, it will prevent routing switching, otherwise it will return a string to prompt the user to decide whether to switch.
(over)