--- 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 | Routing Advanced ## Learning Goals After this lesson, you will be able to: - Pass parameter through routes using `react-router-dom` - Render different page depending on the value of URL `params` - Retrieve strings from URLs ## Introduction After talking about the basics of routing in React, in this lesson we will tackle some advanced topics we should take in account when using `react-router-dom` on our apps. We will start of with explaining how to access URL `params` that we leave in URLs. :::info lecture Jusque lĂ , nous avons vu comment "changer de page" dans une SPA entre notre HOME `/` et notre page `/about` dans l'exemple prĂ©cĂ©dent. Il nous reste maintenant Ă  voir comment rĂ©cupĂ©rer des paramĂštres transmis dans l'URL, comme par ex: l'id d'un projet `/projects/1234` pour en afficher le dĂ©tail... ::: Let's work on the `<Projects />` component that you built toward the end of the last class. Probably you will have to update your example a bit so it corresponds with our example but nothing to worry about, in any case we will provide the code. So inside `components` folder, you created `Projects.js` and the main goal is to display them practicing basic concepts of *routes*: :::info lecture Retravaillons notre composant `Projects.js` : ::: ```jsx // components/Projects.js import React from 'react'; export const myProjects = [ { id: "1a", name: "The Frogger Clone", year: 2017, technologies: "JavaScript, jQuery", description: "The first project game clone." }, { id: "2b", name: "iTravel", year: 2017, technologies: "Mongo DB, ExpressJS, NodeJS, JavaScript, HTML, CSS", description: "Web App that allows logged in users to share their experiences about travel destinations." }, { id: "3c", name: "The Plan", year: 2017, technologies: "Mongo DB, ExpressJS, Angular2, NodeJS, JavaScript, HTML, CSS", description: "Web App that allows logged in users to plan your next activity with your friends or business partners." } ] export const projects = () => { return ( <div> <h2>Projects:</h2> {myProjects.map((eachProject, index) => { return ( <div key={eachProject.id}> <h3> {eachProject.name} </h3> <h4>{eachProject.technologies}</h4> <hr /> </div> ) })} </div> ) } ``` :::info lecture Remarquons qu'ici, nous exportons 2 choses (grĂące aux `named-export` VS. `default-export`) : - le tableau `myProjects` - et le composant `projects` NB: en rĂ©alitĂ©, on taperait une API pour `myProjects` avec axios... ::: Let's quickly go over `Projects.js`. As you can see, we export **myProjects** array because eventually we will need to access to it from `ProjectDetails` component. In real-world apps you probably won't have need to do this because you will fetch the data from APIs using *axios* or some other way. Since we haven't learned how to integrate the backend yet, we will use a simple array. So now let's first update our `<Navbar />` with the new `<Link />`: :::info lecture VĂ©rifions que notre `<Navbar />` comporte bien un lien vers notre composant : ::: ```jsx // components/Navbar.js ... <ul> ... <li><Link to='/projects' style={{ textDecoration: 'none' }}>Projects</Link></li> </ul> ... ``` Okay, now we have to include `<Projects />` component onto the DOM. In `app.js` import the `Projects.js` and inside the `<Route />` use it to be visible when we click on the `link`. :::info lecture VĂ©rifions aussi qu'une route `/projects` est bien dĂ©finie et qu'elle pointe bien vers le bon `named-export` du module `Projects.js`: ::: ```jsx // App.js import {projects as Projects} from './components/Projects'; ... <Switch> ... <Route exact path='/projects' component={Projects}/> </Switch> ... ``` :::warning lecture Remarquons l'utilisation d'une prop `exact`... ::: :::info Side note: `import {projects as Projects}` is very common way of importing functional components that are being exported from files with multiple exports, which means, they are not exported as `export default`. Remember that we said, when we export only one function/variable from the file, we use *export default* way. However, when we have multiple exports, we use just `export` keyword and when we import them in some other file we have to use the same name we gave them originally and also they need to be wrapped up in `{ }`. ::: Okay, now we have the link in the navbar that takes us to `/projects` and we can see our projects' name and technologies used there. The fun starts now 😉 We will turn the each project's name into clickable link that will take us to separate, `details page` for each project. Now we will talk about using `params`. Remember how we use *params* to get the ids from URLs in *Express*? The logic is the same here, the way is bit different. :::info lecture Nous allons maintenant faire de chaque projet, un lien cliquable vers une page "dĂ©tail" de notre application: `/projects/1a` ... ::: ### URL Parameters Route parameters are parts of the URL that will change based on the object we want to display. For example, if we wanted to view information about project #1, we would visit the path `/projects/1a`. However, if we want to view information on project #2, we would visit `/projects/2b`. That last part of the URL is the `parameter`. Same as we did on Express, with `react-router-dom`, we designate a dynamic portion of the URL to be matched by putting a colon `(:)` before it. Our first step will be making the project name as link and see how that changes the URL. Inside `Projects.js` update the line with project name: :::info lecture Dans la liste de nos projets, intĂ©grons grĂące Ă  `<Link to=>` un lien pour chq projet : ::: ```jsx // components/Projects.js ... import { Link } from 'react-router-dom'; // 👈 ADD ... // export const myProjects => all this stays the same, we are updating only `eachProject.name` line export const projects = () => { return ( <div> <h2>Projects:</h2> {myProjects.map((eachProject, index) => { return ( <div key={eachProject.id}> <h3> {/*👇 CHANGE */} <Link to={`/projects/${eachProject.id}`}>{eachProject.name}</Link> </h3> <h4>{eachProject.technologies}</h4> <hr /> </div> ) })} </div> ) } ``` :::warning lecture Et une nouvelle `<Route>` `/projects/:id` vers un composant a venir `ProjectDetails` : ::: ```jsx // App.js ... import ProjectDetails from './components/ProjectDetails'; // 👈 class App extends Component { render() { return ( <div className="App"> ... <Switch> ... {/* 👇 */} <Route exact path="/projects/:id" component={ProjectDetails} /> </Switch> </div> ); } } export default App; ``` Now when we click on each project name, we see that the URL is changing `dynamically` as we click. Each of these URLs will display data about different projects so we need new component that will hold that information. Inside `components` folder create `ProjectDetails.js` and import `myProjects` array that we exported from `Projects.js` ( this component will also need access to it, that's why exported them 👍 ). :::info lecture CrĂ©ons maintenant ce dernier composant `ProjectDetails` : ```shell $ touch src/components/ProjectDetails.js ``` ::: ```jsx // components/ProjectDetails.js import React, { Component } from 'react'; import { myProjects } from './Projects'; const projectDetails = (props) => { console.log(props); // 👈 inspectons ce qui sera passĂ© en paramĂštres return ( <div>TODO project details</div> ); } export default projectDetails; ``` If we check our console, we will see that because we are using `react-router-dom` we can see all of this: `{match: {
}, location: {
}, history: {
}, staticContext: undefined}` and if we expand this object, we actually see that we need part that's nested inside `match` and that is `params`. Great! So our next step is to take that `id`, which we get from **`props.match.params.id`** and find the project that has the id. Let's do it: :::info lecture Nous voyons qu'un gros objet: ```javascript { match: {
}, location: {
}, history: {
}, staticContext: undefined }` ``` est passĂ© au composant par la `<Route>`. ::: :::info lecture Maintenant que nous savons rĂ©cupĂ©rer le paramĂštre dans `props.match.params.id`, terminons notre composant : ::: ```jsx // components/ProjectDetails.js import React from 'react'; import { myProjects } from './Projects'; import { Link } from 'react-router-dom'; const projectDetails = (props) => { console.log(props) // // Fonction utilitaire retrouvant un projet en particulier (.find) // const getProject = (id) => { return myProjects.find(project => project.id === id) }; const foundProject = getProject(props.match.params.id); return ( <div> <h2>{ foundProject.name } <span style={{fontSize:"14px"}}>{ foundProject.year }</span></h2> <h3>Used technologies: { foundProject.technologies }</h3> <p>{ foundProject.description }</p> <Link to='/projects'>Back</Link> </div> ) } export default projectDetails; ``` Not hard at all, right? Extracting the *id* from the URL is the most common use case and it will help you a lot. When used inside `stateful` components, make sure you're using `this` keyword: `this.props.match.params.id`. #### Example To see different example, you can visit: [React Training](https://reacttraining.com/react-router/web/example/url-params) ## Extras :::info lecture Parler de l'alternative `render={(props) => {}}`: ```jsx <Route path="/projects/:id" render={(props) => { const id = props.match.params.id; const project = json.find(p => p.id === id); return ( <ProjectDetails {...project}> ); }} ``` qui nous permet d'appeler nous-meme le composant en lui passant ce qu'on veut. ::: ### Query String :::info lecture Si on a besoin de retrouver d'autres paramĂštres issus d'une query-string, on utilisera [un parser spĂ©cialisĂ©](https://www.npmjs.com/package/query-string) sur `props.location.search` ::: When you’re building for the web, if you want to pass information via the URL (and it doesn’t make sense to use a `URL` parameter), you’d use a **query string.** **https://www.booking.com/search?place=Barcelona&destType=hotel** Let's take a look to that URL. It has 2 query strings: `place` and `destType`. This way, Booking is using the query string to filter the place and type of room you are looking. Previously in this lesson, when we console logged *props*, we saw that beside *match* object, there's also `location` object. If we dig dipper, we can see that the *location* object has `search` property on it and this is the property we actually need. In hypothetical case, if we have just functional component, we would do this: ```js console.log(props.location.search); ``` or inside `class`, we would do this: ```jsx componentDidMount() { console.log(this.props.location.search); } ``` and in any case, we would see this: `?place=Barcelona&destType=hotel"` Awesome, but this is the literal query string. You’ll need to somehow parse it before you can get the actual values. You may be surprised to hear that React Router doesn’t come with built in support for parsing query strings, so we use an `npm` package that helps us with this: ```bash $ npm install --save query-string ``` With that library installed, all we need to do is call `queryString.parse` passing in our `location.search`. That will parse the query string into an object and than we can get access to the properties and its values. ```js import queryString from 'query-string' ... componentDidMount() { const values = queryString.parse(this.props.location.search) console.log(values.place); // "Barcelona" console.log(values.destType); // "hotel" } ``` ## Summary On this learning unit we dive deeper on React Routing. URL Parameter and Query Strings are really important concepts you will be using a lot as React Developers, so it is super important to fully understand how to implement them. ## Extra Resources - [Query String NPM](https://www.npmjs.com/package/query-string) - [React Router Training](https://reacttraining.com/react-router) - [More Training](https://github.com/ReactTraining)