We have learnt and coded a lot over the past 8 sections. But it feels like something is still missing, right? We can’t navigate to different pages! What if we want to write a personal website, and let’s say, we want our homepage to be on “/” and our blog posts to be on “/blogs”?
Today we’ll talk about another library that’s commonly used with React - React Router!
npm install --save react-router-dom
There are a few versions of React Router. For example, “react-router-dom” is for browsers, while “react-router-native” is for native mobile applications. So what you are learning today will translate perfectly to when you want to develop mobile applications one day!
import { BrowserRouter } from 'react-router-dom';
<BrowserRouter>
<App />
</BrowserRouter>
Our index.js should look something like:
import React from 'react';
import ReactDOM from 'react-dom';
import { BrowserRouter } from 'react-router-dom'
import App from './App';
import registerServiceWorker from './registerServiceWorker';
ReactDOM.render((
<BrowserRouter>
<App />
</BrowserRouter>
), document.getElementById('root'))
registerServiceWorker();
import React, { Component } from 'react';
class App extends Component {
render() {
return (
);
}
}
export default App;
import { Route } from 'react-router-dom';
<Route path='/blog' component={Blog}/>
import React, { Component } from 'react';
class Blog extends Component {
render() {
return <h1>Blog</h1>;
}
}
export default Blog;
import Blog from './Blog';
And we’re done!
render() {
return (
<div>
<Route path='/blog' component={Blog}/>
<Route path='/blog/1’ component={Post}/>
</div>
);
}
This is because react-router considers both “/blog” and “/blog/1” to match “/blog” because both of them starts with the “/blog” path. Therefore, when you visit “/blog/1”, both the routes “/blog” and “/blog/1” are matched, and both <Blog/> and <Post/> are rendered. This might be the desired behavior in some cases, but in most cases, we only want ONE component to be rendered for each path. How do we solve this issue?
import { Route, Switch } from 'react-router-dom';
<Switch>
<Route path='/blog' component={Blog}/>
<Route path='/blog/1’ component={Post}/>
</Switch>
If you go back to your browser, you’ll find a new problem: you will see <Blog/> when you visit both “/blog” and “/blog/1”! This is because as we said above, “/blog/1” matched with the “/blog” path. Therefore, the first route is triggered, rendering <Blog/>. In other words, the second route will never be reached no matter which path you visit!
<Switch>
<Route path='/blog/1’ component={Post}/>
<Route path='/blog' component={Blog}/>
</Switch>
<Switch>
<Route path='/blog/1’ component={Post}/> ←
<Route path='/blog' component={Blog}/>
</Switch>
So if we visit “/blog/1”, <Switch> will see that it matches the first route, therefore <Post/> will be rendered.
If instead we visit “/blog”, <Switch> will start from the first route:
<Switch>
<Route path='/blog/1’ component={Post}/> ←
<Route path='/blog' component={Blog}/>
</Switch>
And sees that it doesn’t match the first route, and it will move on to the second route:
<Switch>
<Route path='/blog/1 component={Post}/>
<Route path='/blog' component={Blog}/> ←
</Switch>
And sees that it matches the second route, therefore <Blog/> will be rendered.
<Switch>
<Route exact path='/blog' component={Blog}/>
<Route path='/blog/1’ component={Post}/>
</Switch>
Now, our “/blog” route is triggered only when we visit exactly “/blog”, nothing else. I personally think this is a better style, because it is clearer and more readable!
<Switch>
<Route exact path='/blog' component={Blog}/>
<Route path='/blog/:postid’ component={Post}/>
</Switch>
Notice we appended “:postid” after “/blog/”. Now anything we put after “/blog/” will be stored as “postid”!
render(){
return <h1>{‘Blog ’ + this.props.match.params.postid}</h1>
}
React Router automatically passes in an object “match” as props to our <Post/> component. The “params” property inside the match object contains all the parameters we passed to the route. Therefore to access the “postid” parameter, we can access this.props.match.params.postid.
Just printing the postid on the screen is stupid, I admit. This is just an example. In real applications, you would normally render different things according to the parameter. For example, we could take our post id, fetch the post from the database with the post id, then display it in your website!
Wait a second, I hear you ask, we could just use an anchor tag right?
<a href=”/blog”>Blog</a>
That works, but it would cause your browser to reload. That’s not very good in terms of user experience. Luckily, React Router offers the <Link> component. Let’s test it out!
import { Route, Switch, Link } from 'react-router-dom';
<Link to=”/path”>Click here</Link>
<Link to=”/”>Home</Link>
<Link to=”/blog”>Blog</Link>
render() {
return (
<div>
<h1>Blog</h1>
<ol>
<li><Link to='/blog/1'>Blog 1</Link></li>
<li><Link to='/blog/2'>Blog 2</Link></li>
<li><Link to='/blog/3'>Blog 3</Link></li>
</ol>
</div>
);
}
Head back to the browser and have fun clicking around!
Well done! In this section, we first looked into setting up React Router. Then, we learnt to define routes in React Router. They looked something like:
<Route exact path='/pathname’ component={ComponentName}/>
Then, we talked about iterating through routes with <Switch>. After that, we looked into passing parameters to the URL and accessing it from this.props.match.params.*parameter name*. Finally, we learnt to add links to our webpage with <Link>.
Here is the code for this section.