--- tags: ironhack, lecture, --- <style> .markdown-body img[src$=".png"] {background-color:transparent;} .alert-info.lecture, .alert-success.lecture, .alert-warning.lecture, .alert-danger.lecture { box-shadow:0 0 0 .5em rgba(64, 96, 85, 0.4); margin-top:20px;margin-bottom:20px; position:relative; ddisplay:none; } .alert-info.lecture:before, .alert-success.lecture:before, .alert-warning.lecture:before, .alert-danger.lecture:before { content:"👨‍🏫\A"; white-space:pre-line; display:block;margin-bottom:.5em; /*position:absolute; right:0; top:0; margin:3px;margin-right:7px;*/ } b { --color:yellow; font-weight:500; background:var(--color); box-shadow:0 0 0 .35em var(--color),0 0 0 .35em; } .skip { opacity:.4; } </style> ![logo_ironhack_blue 7](https://user-images.githubusercontent.com/23629340/40541063-a07a0a8a-601a-11e8-91b5-2f13e4e6b441.png) # React | Intro to Routing ## Learning Goals After this lesson, you will be able to: - Understand how to use `react-router-dom`. - Create routes inside your React app. - Understand how to set dynamic routes for your app. ## Introduction Using React we create Single Page Apps, but that doesn't necessarily imply we won't need some routes in our applications. The SPAs are easy to deploy and significantly improve the user experience, among other advantages. They provide fluid navigation experience for users, but although they are *SPA* some routing features of traditional websites are expected. For example, users should be able to bookmark the page which requires that the pages should have specific URLs. Besides that, the forward and back button should move me forward or backward in the browsing history. :::warning lecture SPA marche tellement bien, qu'on a plus qu'une seule URL, mais quid : - share URL (bookmark, send to a friend...) - previous button, history ::: React has its own way of dealing with this, and that is accomplished by using `React Router` library. Actually, we will be using just its part, **react-router-dom** npm package. React router library comprises of three packages: `react-router`, `react-router-dom`, and `react-router-native`. *react-router* is the core package for the router, whereas the other two are environment specific. You should use `react-router-dom` if you’re building for the web, and `react-router-native` if you’re on a mobile app development environment using React Native. :::warning lecture Nous allons pour cela utiliser un module NPM : [React Router](https://reacttraining.com/react-router/) et particulièrement [`react-router-dom`](https://www.npmjs.com/package/react-router-dom) ::: :::info lecture En coulisse, c'est l'[History API](https://developer.mozilla.org/en-US/docs/Web/API/History/pushState) qui est utilisé en JavaScript (côté client) pour "simuler" un changement de page, en changeant l'URL. ::: --- To start of this lesson, let's create the new React app in which we will simulate portfolio web site that will have couple different pages and users will be able to go through them separately. :::info lecture Créons un petit projet rapide : `portfolio-with-routes` ::: ```bash $ npx create-react-app portfolio-with-routes ``` Inside our project folder, add the `react-router-dom`: :::info lecture avec `react-router-dom` : ::: ```bash $ npm install react-router-dom ``` ## Routers A router allows your application to navigate between different components, changing the browser URL, modifying the browser history, and keeping the UI state in sync. :::info lecture Le router va faire le lien entre la barre d'adresse et l'état de notre application (où l'on se trouve, quel composant a été rendu...) ::: :::success lecture Finalement comme si nous étions sur une appli avec rafraichissements de pages, où l'URL représente l'état de l'application. ::: ### React Router The **React Router** is based on three main components: :::info lecture 3 composants principaux : ::: - **`<Router>`** - This component keeps the *User Interface in sync with the URL*, - **`<Link>`** - Renders a navigation link. (basically an `<a>` tag, but they *change the URL without refreshing the page*), - **`<Route>`** - *Renders a UI component* depending on the URL. The first thing we need to do is to choose a `<Router>` implementation; for web applications, we have two options: :::info lecture 2 implementations du router (DOM) : ::: - **`<BrowserRouter>`** - Uses the HTML5 History API*, - **`<HashRouter>`** - Uses the hash portion of the URL. (Only for older browsers that don't support the HTML5 History API, so let's focus on the `<BrowserRouter>`) :::info The History API is an object that lets us manage the current `location` via `history.location` as well as previous locations. Think of the location property of the object as an array. The current location is the last item on the array and we manipulate the array through methods such as `history.push()` or `history.replace()`. Whatever manipulation is made on the array will trigger a page transition to the current location. This is what happens behind the scene when using `Link` as we will see soon. ::: ### Dynamic Routing When we say dynamic routing, we mean routing that takes place as your app is rendering. We will start building our app by adding the `components` folder inside `src` folder. Our portfolio app will have `Home.js`, `About.js`, `Education.js` and `Experience.js` files so let's add them into the *components* folder. :::info lecture Dans un premier temps nous allons créer tous les composants de notre application de portfolio : ```shell $ mkdir src/components $ touch src/components/{Home,About,Education,Experience}.js $ ls src/components ``` ::: We won't stress the routes yet, but we will focus on creating these components. Let's start with building `<Home />` component. :::info lecture Créons `src/components/Home.js` comme suit : ::: ```jsx // components/Home.js import React from 'react'; const home = () => { return ( <div> <div> <h3>Welcome to my portfolio page! My name is</h3> <h1>Ana</h1> <p>and I'm Web Developer!</p> </div> </div> ) } export default home; ``` Easily we can display this component just if we import it into `App.js` and render it to the DOM. For now, let's do that. :::info lecture Instancions-le dans `<App>` : ::: ```jsx // App.js import React, { Component } from 'react'; import './App.css'; import Home from './components/Home'; // 👈 class App extends Component { render() { return ( <div className="App"> <Home /> {/* 👈 */} </div> ); } } export default App; ``` One component is not enough to demonstrate how the routes work so let's keep building. Next we will add `<Education />` component: :::info lecture Un 2e composant au moins est nécessaire pour naviguer parmi plusieurs. Créons `src/components/Education.js` comme suit : ::: ```jsx // components/Education.js import React from 'react'; const education = () => { const myEducation = [ { schoolName:"Ironhack", city: "Miami", country:"US", degree:"Full-Stack Web Developer", schoolStarted:"2017", schoolEnded:"2017" }, { schoolName:"University of Cool Things", city: "Paris", country:"France", degree:"MS of Cool Things", schoolStarted:"2015", schoolEnded:"2017" }, { schoolName:"University of Chill Vibe", city: "Berlin", country:"Germany", degree:"BS of Super Science", schoolStarted:"2012", schoolEnded:"2015" } ] return ( <div> <h2>Education:</h2> {myEducation.map((eachSchool, index) => { return ( <div key={index}> <h3>{eachSchool.schoolName}</h3> <p>{eachSchool.city}, {eachSchool.country}</p> <h4>{eachSchool.degree}</h4> <p>{eachSchool.schoolStarted} - {eachSchool.schoolEnded}</p> </div> ) })} </div> ) } export default education; ``` :::info lecture Créons maintenant `<Experience>` : ::: And then keep building `<Experience />` component: ```jsx // components/Experience.js import React from 'react'; const experience = () => { const placesWhereIWorked = [ { name:"Cool company", city: "Barcelona", country:"Spain", jobTitle:"Specialist for cool things", jobDescription:"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua", jobStarted:"2017", jobEnded:"" }, { name:"Amazing company", city: "Madrid", country:"Spain", jobTitle:"Junior for cool things", jobDescription:"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua", jobStarted:"2015", jobEnded:"2017" }, { name:"Cool company", city: "Amsterdam", country:"Netherlands", jobTitle:"Associate Super Intern", jobDescription:"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua", jobStarted:"2013", jobEnded:"2015" } ] const checkJobEnded = (end) => { let endYear; if (end === ""){ endYear = "current"; } else { endYear = end; } return endYear; } return ( <div className=""> <h2>Work Experience:</h2> {placesWhereIWorked.map((eachPlace, index) => { return ( <div className="experience-content" key={index}> <h3>{eachPlace.name}</h3> <p>{eachPlace.city}, {eachPlace.country}</p> <h4>{eachPlace.jobTitle}</h4> <p>{eachPlace.jobDescription}</p> <p>{eachPlace.jobStarted} - {checkJobEnded(eachPlace.jobEnded)}</p> </div> ) })} </div> ) } export default experience; ``` :::info lecture Regroupons `<Education>` et `<Experience>` dans un **`<About>`** : ::: We don't have to build the whole app in *routes* manner - each component doesn't have to be rendered on separate page. We will integrate *<Education />* and *<Experience />* inside `About.js`: ```jsx // components/About.js import React from 'react'; import Experience from './Experience'; import Education from './Education'; const about = () => { return ( <div> <div style={{width: '40%', float:"left"}}> <Education/> </div> <div style={{width: '60%', float:"right"}}> <Experience /> </div> </div> ) } export default about; ``` We can add `<About />` component inside `App.js`. :::info lecture Pour finalement instancier `<About>` dans notre `<App>` à côté de `<Home>` : ::: ```jsx // App.js ... import Home from './components/Home'; import About from './components/About'; ... return ( <div className="App"> <Home /> <About /> </div> ); ``` --- :::info lecture Notre appli est en place, implémentons maintenant le routing... ::: At this point we can start implementing routes. We are showing everything on the same page, but we will break it down to separate pages. Remember how we said that our first task is to decide weather we will use `<BrowserRouter>` or `<HashRouter>`, and that nowadays, we mostly use `<BrowserRouter>`. In order to do so, first we will update `index.js`. :::info lecture Dans `index.js`, on entoure `<App>` du `<Router>`: ::: ```jsx // index.js import React from 'react'; import ReactDOM from 'react-dom'; import './index.css'; import App from './App'; import registerServiceWorker from './registerServiceWorker'; import { BrowserRouter as Router } from 'react-router-dom'; // 👈 ReactDOM.render( ( <Router> <App /> </Router> ), document.getElementById('root') ); ``` :::info It’s important to mention that a `<Router>` component can only have one child element. In our example, there's only one `<div>` tag as a child. For example this code: ```js // ... ReactDOM.render( <Router> <div>First child!</div> <div>Second child!</div> </Router> , document.getElementById('root') ); // ... ``` throws this error message: ![image](https://user-images.githubusercontent.com/23629340/43824987-cdcb7546-9af3-11e8-8453-0429ea9b3a5a.png) ::: The main job of a `<Router>` component is to create a history object to keep track of the location(URL). When the location changes because of a navigation action, the child component is re-rendered. Most of the time, you’ll use a `<Link>` component to change the location. Great, we have built the base. Let's move forward. We need some `links` and preferably, we need them in `nav` bar. We can do so by building the next component `<Navbar />` inside `components` folder. Here we will see the `<Link>` component, which we import from *react-router-dom*, and it represents a replacement for standard links. Wherever you render a `<Link>`, an anchor (`<a>`) will be rendered in your application’s HTML. :::info lecture Ajoutons un composant `<Navbar>` qui va nous permettre de naviguer parmi les "pages" de notre SPA On utilise pour cela le composant `<Link to="/maroute">` de `react-router-dom` ::: ```jsx // components/Navbar.js import React from 'react'; import { Link } from 'react-router-dom'; const navbar = () => { return ( <nav className="nav-style"> <ul> <li><Link to='/'>Home</Link></li> <li><Link to='/about'>About</Link></li> </ul> </nav> ) } export default navbar; ``` :::warning lecture Pourquoi `<Link to=>` plutôt que de vulgaires `<a href=` ? ::: And also add some styles: :::info lecture +Un peu de style dans `src/App.css` : ::: ```css .nav-style { text-align: right; } .nav-style li { list-style: none; display: inline; margin: 0 10px; } .nav-style a { text-decoration: none; } ``` And final addition is to add `routes` inside `App.js` because that is where the components are being rendered. :::info lecture - Instanciation de `<Navbar>` - Ajout de `<Switch>` et `<Route path="/maroute" component={}>` ::: ```jsx // App.js import React, { Component } from 'react'; import './App.css'; import Home from './components/Home'; import About from './components/About'; import Navbar from './components/Navbar'; import { Switch, Route } from 'react-router-dom'; class App extends Component { render() { return ( <div className="App"> <Navbar /> {/* 👈 commun aux deux */} <Switch> {/* 👈 rend 1 seule des 2 routes */} <Route exact path='/' component={Home}/> <Route path='/about' component={About}/> </Switch> </div> ); } } export default App; ``` :::danger lecture Importance du `exact` dans la première route, car sans, si je vais sur `http://localhost:3000/coucou`, `/` matcherait. cf: https://reacttraining.com/react-router/web/api/Route/exact-bool ::: [`<Switch>`](https://reacttraining.com/react-router/web/api/Switch) returns only <b>the first matching route</b>. :::info lecture En exercice, ajoutons un composant `src/components/Projects.js` pour lister nos différentes réalisations. ::: Now that you have an example how routes work and should be organized, let's practice. Add new component, `Projects.js` that will show list of all projects you made and render it through a third route. ### Extras #### `<NavLink>` :::info lecture [`<NavLink>`](https://reacttraining.com/react-router/web/api/NavLink) est une version "enrichie" de `<Link>` permettant des options supplémentaires : `activeClassName`, `activeStyle`, `exact`, `strict` ::: The `<NavLink>` is a special type of `<Link>` that can style itself as “active” when its to prop matches the current location. In order to use `<NavLink>`, we just have to import `<Link />` from `react-router-dom`. *<NavLink>* can be used with some attributes: - **`activeClassName: string`**. The class to give the element when it is active. The default given class is active. This will be joined with the className prop. ```jsx <NavLink to="/faq" activeClassName="selected">FAQs</NavLink> ``` - **`activeStyle: object`**. The styles to apply to the element when it is active. ```jsx <NavLink to="/faq" activeStyle={{fontWeight: 'bold', color: 'red' }}>FAQs</NavLink> ``` - **`exact: boolean`**. When true, the active class/style will only be applied if the location is matched exactly. ```jsx <NavLink exact to="/profile">Profile</NavLink> ``` - **`strict: boolean`**. When true, the **trailing slash** on a location’s pathname will be taken into consideration when determining if the location matches the current URL. ```jsx <NavLink strict to="/events/">Events</NavLink> ``` #### `<Redirect>` :::info lecture Permet de rediriger l'application vers une autre route, typiquement si pas loggué : ::: Rendering a `<Redirect>` will navigate to a new location. The new location will override the current location in the history stack, like server-side redirects **(HTTP 3xx)** do. ```jsx import { Route, Redirect } from 'react-router' <Route exact path="/" render={() => ( loggedIn ? ( <Redirect to="/dashboard"/> ) : ( <PublicHomePage/> ) )}/> ``` - **`to: string`**. The URL to redirect to. Any valid URL path that path-to-regexp understands. All URL parameters that are used in to must be covered by from. - **`push: bool`**. When true, redirecting will push a new entry onto the history instead of replacing the current one. ```jsx <Redirect push to="/somewhere/else"/> ``` ## Summary We learned the basics for adding routes to our React applications. As we mentioned, we use React to build SPA, which brings us the challenge of incorporating routes. But as we know, we need them and at this point we know how to incorporate them. We use `Router`, that allows our apps to navigate between different components, changing the browser URL, modifying the browser history, and keeping the UI state in sync. In the following lesson, we will see more advanced topics about routing! :muscle: ## Extra Resources - [React Training Guide](https://reacttraining.com/react-router/web/guides/philosophy) - [React Training - Router](https://github.com/ReactTraining/react-router)