⬆️ ⬇️

Simple React Router v4 Tutorial

image



By @pshrmnOriginal articleReading time : 10 minutes



React Router v4 is a redesigned version of the popular React add-on. Platform-dependent route configurations from the previous version have been removed and now everything is a simple component.



This tutorial covers everything you need to create websites with React Router. We will create a site for a local sports team.

')

Want to watch a demo?







Installation



React Router v4 was broken into 3 packages:



react-router 


 router-dom 


 react-router-native 


react-router provides basic functions and components for working in two environments (Browser and react-native)



We will create a site that will be displayed in the browser, so we should use react-router-dom . react-router-dom exports all functions from react-router so we only need to install react-router-dom .



 npm install --save react-router-dom 


Router



When starting a project, you need to determine which type of router to use. For browser projects there are BrowserRouter and HashRouter components. BrowserRouter - should be used when you are processing dynamic requests on the server, and use HashRouter when you have a static web site.



Usually it is preferable to use BrowserRouter , but if your site is located on a static server ( from translation as github pages ), then using HashRouter is a good solution to the problem.

Our project involves the use of backend so we will use the BrowserRouter .



History



Each Router creates a history object that stores the path to the current location [1] and redraws the site interface when some path changes occur.



The remaining functions provided by React Router rely on the availability of the history object through the context, so they must be rendered inside the Router component.



Note: The React Router components that do not have the Router component as their ancestor will not work, as the context will not be available.



Rendering Router



The Router component expects only one item as a child. To work within the framework of this condition, it is convenient to create the <App /> component that renders your entire application (this is also important for server rendering).



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


App component



Our application starts with the <App/> component which we divide into two parts. <Header/> which will contain the navigation links and <Main/> which will contain the content of the routes.



 //        <Router> const App = () => ( <div> <Header /> <Main /> </div> ) 


Routes



<Route/> component is the main building block of React Router. In that case, if you need to render an element depending on the pathname of URLs, then you should use the <Route/> component



Path - path



<Route /> takes a path in the form of prop which describes a specific path and maps to the location.pathname.



 <Route path='/roster'/> 


In the example above, <Route/> matches the location.pathname that starts with / roster [2]. When the current location.pathname is mapped positively to the prop path, the component will be rendered, and if we cannot match them, then Route does not render anything [3].



 <Route path='/roster'/> //  location.pathname  '/', prop path   //  location.pathname  '/roster'  '/roster/2', prop path  //   exact prop.     '/roster',   // '/roster/2' <Route exact path='/roster'/> 


Note: When it comes to the path, the React Router only thinks about the path without a domain. This means that in the address:



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


React Router will only see /my-projects/one



Path mapping



The npm path-to-regexp package compiles a prop path into a regular expression and matches it against location.pathname. Path lines have more complex formatting options than explained here. You can read the documentation .



When paths are matched, a match object is created that contains properties:





Note: You can play around with the route tester and see how a match object is created.



Note: path in Route must be absolute [4].



Creation of our routes



The Route component can be anywhere in the router, but sometimes you need to determine what to render in the same place. In this case, you should use the Route grouping component <Switch/> . <Switch/> iteratively passes through child components and renders only the first one that matches location.pathname.



On our website, the ways we want to map are:





In order of matching paths in our application, all we need to do is create a Route component with the prop path that we want to match.



 <Switch> <Route exact path='/' component={Home}/> {/*  /roster  /roster/:number   /roster */} <Route path='/roster' component={Roster}/> <Route path='/schedule' component={Schedule}/> </Switch> 


What does the render of the Route component do?



Route has 3 props, which describe how to render by matching the prop path with the location.pathname and only one of the prop must be presented in the Route:





 <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}/> )}/> 


In typical situations, use component or render. Children prop can be used, but it is better to do nothing if the path does not match the location.pathname.



A few props will be transferred to the rendered Route item. match is the path mapping object with location.pathname , the location object [6] and the history object (created by the route itself) [7].



Main



Now we describe the basic structure of the router. We just need to display our routes. For our application, we will use the <Switch/> component and the <Route/> component inside our <Main/> component that will put the generated HTML that satisfies the path mapping inside.



<Main/> DOM node (node)

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


Note: Route for the main page contains prop exact , thanks to which the paths are compared strictly.



Inherited Routes



Player profile /roster/:number not included in <Switch/> . Instead, it will be rendered by the <Roster/> component that is rendered whenever the path starts with /roster .



In the Roster component, we will create components for two paths:





 const Roster = () => ( <Switch> <Route exact path='/roster' component={FullRoster}/> <Route path='/roster/:number' component={Player}/> </Switch> ) 


It may be useful to group routes that have common components, which simplifies parent routes and allows you to display content that belongs to several routes.



For example, <Roster/> can be rendered with a header that will be displayed in all routs that start with /roster .



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


Parameters in path



Sometimes we need to use variables to get some information. For example, a player’s profile profile where we need to get a player’s number. We did this by adding a parameter to the prop path .



:number part of the string in /roster/:number means that the part of the path after /roster/ will be received as a variable and saved in match.params.number . For example, the path /roster/6 generate the following object with parameters:



 { number: '6' //      } 


The <Player/> component will use props.match.params to get the necessary information that needs to be rendered.



 // API        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> ) 


Note: You can learn more about the parameters in the paths in the path-to-regexp package.



Along with the <Player/> component, our website uses others like <FullRoster/> , <Schedule/> and <Home/> .



 const FullRoster = () => ( <div> <ul> { PlayerAPI.all().map(p => ( <li key={p.number}> <Link to={`/roster/${p.number}`}>{p.name}</Link> </li> )) } </ul> </div> ) const Schedule = () => ( <div> <ul> <li>6/5 @ </li> <li>6/8 vs </li> <li>6/14 @ </li> </ul> </div> ) const Home = () => ( <div> <h1>    !</h1> </div> ) 


Links



The final touch, our site needs to navigate between pages. If we create regular links then the page will reload. React Router solves this problem with the <Link/> component that prevents the reboot. When we click on <Link/> it updates the URL and the React Router renders the necessary component without refreshing the page.



 import { Link } from 'react-router-dom' const Header = () => ( <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> ) 


<Link/> uses prop to describe the URL where to go. Prop to can be a string or a location object (which consists of the pathname, search, hash, state properties). If it is a string, then it is converted to a location object.



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


Note: Paths in <Link/> components must be absolute [4].



Working example



All code of our web site is available at this address on codepen.



Route is ready!



I hope now you are ready to dive into exploring the details of web application routing.



We used the most basic components that you need when creating your own web applications ( <BrowserRouter.>, <Route.>, and <Link.> ), But there are a few more components and props that are not covered here. Fortunately, the React Router has excellent documentation where you can find a more detailed explanation of the components and props. Also, the documentation provides working examples with source code.



Explanations



[1] - The location object describes different parts of the URL



 //  location { pathname: '/', search: '', hash: '', key: 'abc123' state: {} } 


[2] - You can use the <Route/> component without a path. This is useful for passing methods and variables that are stored in context .



[3] - If you use prop children then the route will be rendered, even there is a path and the location.pathname does not match.



[4] - Work is currently underway on relative paths in <Route/> and <Link/> . Relative <Link/> more complex than they might seem, they should be resolved using their parent match object, not the current URL.



[5] - This is a stateless component. Inside there is a big difference between render and component . Component uses React.createElement to create the component, while render used as a function. If you defined the inline function and passed props through it, it would be much slower than using the render function.



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


[6] - The <Route/> <Switch/> components can both use prop location. This allows you to map them to the path, which is actually different from the current URL.



[7] - They also transmit staticContext , but it is only useful when rendering on the server.

Source: https://habr.com/ru/post/329996/



All Articles