React - Introduction to react router v6
Tags: react, javascript, webdev
Published: 2021-12-16 13:02:37

## Installation
After creating our react app we have to install the react router v6 library to our project using npm:

```
npm install react-router-dom@6
```
And import the `BrowserRouter` to our `index.js`:

```javascript
import React from "react";
import ReactDOM from "react-dom";
import "./index.css";
import App from "./App";
import { BrowserRouter } from "react-router-dom";

ReactDOM.render(
  <BrowserRouter>
    <App />
  </BrowserRouter>,
  document.getElementById("root")
);
```

Now our react application can now response to different urls.

## Creating links
To create links on react we could try sing the regular html links and that would lets us navigate to different urls defined in our application but if you try you will realize that each time you click on a link you are reloading the whole page and that's something we don’t do on react as react works as single page application.

Instead we use `Link`  from the `react-router-dom` package we installed earlier. 

```javascript
import { Link } from "react-router-dom";
import "./App.css";
 
const App = () => {
 return <Link to="/hotels">Hotels</Link>;
};
```
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jvzdlpjmlha6g7byblzd.png)

It's discrete but there's a link at the top left of our page but when clicking the page it doesn't get reloaded :) but it’s not showing nothing new :( thou it changes the current url :)

## Defining routes
On my react example app I’ve created 2 pages:

- Hotels list.

- Form to add new hotels to the list.

So I need two routes. One for each page.


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qi4findeon05ip1qobkb.png)

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0smnr4ajfepg5b9tovv2.png)
(follow me for more design tips 😏)

To create a those new routes that matches I will make use of `Routes` and `Route` from the `react-router` package.

```javascript
import { useState } from "react";
import { Route, Routes } from "react-router";
import classes from "./app.module.css";
import Nav from "./components/Nav";
import Form from "./pages/Form";
import Hotels from "./pages/Hotels";

const App = () => {
  const hotelsArray = [
    {
      name: "Hotel Barcelona",
      rooms: 5,
      rating: 8,
    },
    {
      name: "Hotel Paris",
      rooms: 41,
      rating: 9,
    },
    {
      name: "Hotel Munich",
      rooms: 14,
      rating: 10,
    },
  ];

  const [hotels, setHotels] = useState(hotelsArray);

  const newHotelHandler = (hotel) => {
    setHotels((prevState) => [...prevState, hotel]);
  };

  return (
    <>
      <Nav />
      <div className={classes.container}>
        <Routes>
          <Route path="/hotels" element={<Hotels hotels={hotels} />} />
        </Routes>
        <Routes>
          <Route path="/new" element={<Form onSubmit={newHotelHandler} />} />
        </Routes>
      </div>
    </>
  );
};

export default App;
```

On the app I have the hardcoded list of initial hotels to set is as default state and the functions that adds new hotels and updates the state.

To navigate through the pages I’ve created a new component called `nav` that contains all the `Links`:

```javascript
import classes from "./Nav.module.css";
import { Link } from "react-router-dom";

const Nav = () => {
  return (
    <div className={classes.nav}>
      <div className={classes.nav__container}>
        <Link style={{textDecoration: 'none', color: 'white'}} to="/hotels">Hotels</Link>
        <Link style={{textDecoration: 'none', color: 'white'}} to="/new">Add new</Link>
      </div>
    </div>
  );
};

export default Nav;
```

This way now I have a set of urls to navigate on my hotels app:


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ie0sku4rmvlxrea1nz87.gif)

## Params on url
Now I have a list of nice hotels and a form to add new ones but what if I want to check one hotel details. To do that It would be nice to have a parameterized url where to pass the hotel id so my react app can retrieve the hotel details. 

To do it we have to define a new route in our `app.js` . I want that url to be `/hotels/hotelID` so my new route will be defined like this:

```javascript
<Routes>
    <Route path="/hotels/:hotelId" element={<Details hotels={hotels} />} />        
</Routes>
```
And on a new page I will:

1. Read the hotel id from url.
2. Get the hotel details (actually what I will do is get the hotel position on the hotels list).

### Reading the hotel id from the url
To do that we need to import the `useParams` hook from the `react-router-dom`package and read the params:

```javascript
import { useParams } from "react-router-dom";

const Details = () => {
  const params = useParams();

  return <h1>{params.hotelId}</h1>;
};

export default Details;
```

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/392szp2xt0lwkw02v2zg.png)

The params are the ones we've defined in the route path.

### Getting the hotel details
```javascript
import classes from "./Details.module.css";
import { Link, useParams } from "react-router-dom";

const Details = ({ hotels }) => {
  const params = useParams();
  const hotel = hotels[params.hotelId];

  return (
    <div className={classes.container}>
      <h1>Hotel: {hotel.name}</h1>
      <h2>Rooms: {hotel.rooms}</h2>
      <h3>Rating: {hotel.rating}/10</h3>
      <Link to="/hotels">
        <button className={classes.container__save}>Hotels</button>
      </Link>
    </div>
  );
};

export default Details;
```
To access to this url I've updated the hotels list component so each hotel now has a `Link`:
import { Link } from "react-router-dom";
import classes from "./Hotels.module.css";
```javascript
const Hotels = ({ hotels }) => {
  return (
    <div className={classes.container}>
      {hotels.map((hotel, key) => {
        return (
          <Link to={`/hotels/${key}`} style={{textDecoration: 'none', color: 'black'}}>
            <div key={key} className={classes.element}>
              <h1>{hotel.name}</h1>
              <h2>Rooms: {hotel.rooms}</h2>
              <h3>Rating: {hotel.rating}/10</h3>
            </div>
          </Link>
        );
      })}
    </div>
  );
};

export default Hotels;
```javascript

## Navigate programmatically
Sometimes we may need to navigate our users programmatically. If you try the form to add new hotels to the list you will realize that after creating a new hotel you have to manually navigate to the hotels list with the top nav. It works but we can do it better.

At the `Form.js` component we have to import the `useNavigate` hook from the `react-router-dom` package.

```javascript
import { useState } from "react";
import { useNavigate } from "react-router-dom";
import classes from "./Form.module.css";

const Form = ({ onSubmit }) => {
  const navigate = useNavigate();

  const [name, setName] = useState("");
  const [rooms, setRooms] = useState(0);
  const [rating, setRating] = useState(0);

  const nameHandler = (event) => {
    setName(event.target.value);
  };

  const roomsHandler = (event) => {
    setRooms(event.target.value);
  };

  const ratingHandler = (event) => {
    setRating(event.target.value);
  };

  const onSubmitHandler = () => {
      onSubmit({name: name, rooms: rooms, rating: rating});
      // After saving the hotel redirect the user to the hotels list
      navigate('/hotels')
  }

  return (
    <div className={classes.container}>
      <div className={classes.container__field}>
        <label>Hotel Name</label>
        <input onChange={nameHandler} type="text" />
      </div>
      <div className={classes.container__field}>
        <label>Rooms</label>
        <input onChange={roomsHandler} type="number" min="1" max="99" />
      </div>
      <div className={classes.container__field}>
        <label>Rating</label>
        <input onChange={ratingHandler} type="number" min="1" max="10" />
      </div>
      <button onClick={onSubmitHandler} className={classes.container__save}>Save</button>
    </div>
  );
};

export default Form;
```

## Nested routes
Our hotels app now works better but theres another thing we could improve. There's two routes where one is child of another: `/hotels` and `/hotels/:hotelId`. 

On this example they are just two routes but on larger apps this can be annoying so let's nested instead using relative paths:

```javascript
import { useState } from "react";
import { Route, Routes } from "react-router";
import classes from "./app.module.css";
import Nav from "./components/Nav";
import Details from "./pages/Details";
import Form from "./pages/Form";
import Hotels from "./pages/Hotels";

const App = () => {
  const hotelsArray = [
    {
      name: "Hotel Barcelona",
      rooms: 5,
      rating: 8,
    },
    {
      name: "Hotel Paris",
      rooms: 41,
      rating: 9,
    },
    {
      name: "Hotel Munich",
      rooms: 14,
      rating: 10,
    },
  ];

  const [hotels, setHotels] = useState(hotelsArray);

  const newHotelHandler = (hotel) => {
    setHotels((prevState) => [...prevState, hotel]);
  };

  return (
    <>
      <Nav />
      <div className={classes.container}>
        <Routes>
          <Route path="/hotels">
            <Route path="" element={<Hotels hotels={hotels} />} />
            <Route path=":hotelId" element={<Details hotels={hotels} />} />
          </Route>
        </Routes>
        <Routes>
          <Route path="/new" element={<Form onSubmit={newHotelHandler} />} />
        </Routes>
      </div>
    </>
  );
};

export default App;
```

[Here's link to github where you can download the project and try it](https://github.com/josecobo1/react-router-v6) and there's a [demo](https://hotels-list-e37a7.web.app/hotels)