React Router: Updating from v5
Tags: react
Published: 2022-02-12 06:07:07


This is a memo when I updated React Router in my app from v5 to v6. 
Demo app is [here](https://your-library.vercel.app/).

## Switch -> Routes

Use Routes instead of Switch and **exact** keyword is not necessary in v6. Also, element is a new attribute to assign components inside of a Route page.

### v5
```javascript
    <Switch>
     <Route path="/" exact>
      <LaunchPage />
     </Route>
     <Layout>
      <Switch>
       <Route path="/search-book">
        <Home />
       </Route>
       <Route path="/my-books">
        <MyBooks />
       </Route>
      </Switch>
     </Layout>
    </Switch>
```
### v6

A minor finding is that it is impossible to put Layout component inside of Routes. As a result, it is necessary to wrap each page component in Layout, respectively. In addition, “*” is important to let Router know the path has at least one child path.

```javascript
    <Routes>
     <Route path="/" element={<LaunchPage />} />
     <Route path="search-book/" element={<Layout><Home /></Layout>} />   
     <Route path="my-books" element={<Layout><MyBooks /></Layout>}/></Routes>
```
**refactoring
I added WithLayout component and wrapped page components in it to avoid the repeating pattern in Routes.

**WithLayout**
```javascript
    import Layout from "../Layout/Layout.jsx";

    function WithLayout(Child) {
      return function WithLayout(props) {
        return (
          <Layout>
            <Child {...props} />
          </Layout>
        );
      };
    }

    export default WithLayout;
```
**Home**
```javascript
    import SearchBooks from "../components/SearchBooks";
    import WithLayout from "../hoc/WithLayout";

    const Home = () => {
      return <SearchBooks />;
    };
    export default **WithLayout(Home)**;
```
**MyBooks**
```javascript
    import { Fragment, useContext } from "react";
    import MyBookItem from "../components/MyBookItem";
    import SortMyBooks from "../components/SortMyBooks";
    import MyBooksContext from "../store/my-books-context";
    import classes from "./MyBooks.module.css";
    import WithLayout from "../hoc/WithLayout";

    // functions

    const MyBooks = () => {
      // functions

    return (
        <Fragment>
          <SortMyBooks
            onTitle={sortByTitleHandler}
            onDate={sortByDateHandler}
            onRating={sortByRatingHandler}
            onDefault={defaultHandler}
          />
          <div className={classes.box}>
            {myBooksCtx.myBooks.length !== 0 && content}
            {myBooksCtx.myBooks.length === 0 && (
              <p>No books here yet. Let's add your book!</p>
            )}
          </div>
        </Fragment>
      );
    };
    export default **WithLayout(MyBooks)**;
```
**NotFound**
```javascript
    import React from "react";
    import { Link } from "react-router-dom";
    import classes from "./NotFound.module.css";
    import WithLayout from "../hoc/WithLayout";

    function NotFound() {
      return (
        <div className={classes["not-found"]}>
          <h1>404</h1>
          <h2>Page Not Found </h2>
          <p>
            Back to <Link to="/search-book">Home</Link>
          </p>
        </div>
      );
    }

    export default WithLayout(NotFound);
```
**App**
```javascript
    import React, { Fragment, Suspense } from "react";
    import { Route, Routes } from "react-router-dom";
    import LaunchPage from "./pages/LaunchPage";
    import Home from "./pages/Home";
    import LoadingSpinner from "./UI/LoadingSpinner";
    import NotFound from "./pages/NotFound";
    const MyBooks = React.lazy(() => import("./pages/MyBooks"));

    function App() {
      return (
        <Fragment>
          <Suspense
            fallback={
              <div className="centered">
                <LoadingSpinner />
              </div>
            }
          >
            <Routes>
              <Route path="/" element={<LaunchPage />} />
              <Route path="search-book/*" element={<Home />} />
              <Route path="my-books" element={<MyBooks />} />
              <Route path="*" element={<NotFound />} />
            </Routes>
          </Suspense>
        </Fragment>
      );
    }

    export default App;
```
## Full path -> relative path

Use relative path when you add children paths whereas we had to use full path in v5.

### v5
```javascript
    <Route path={match.url} exact>
     //*elements*
    </Route>
    <Route path={`${match.url}/add-my-books`}>
      <AddMyBooks />
    </Route>
```
### v6
```javascript
    <Routes>
     <Route path="" element={
      <BookDetailComponent path={location.pathname} />
      }/>
     <Route path="add-my-books" element={<AddMyBooks />} />
    </Routes>
```
## useRouteMatch -> useLocation

According to the official document, useMatch is the alternative to useRouteMatch. However, useLocation can be an alternative depending on the way of using useRouteMatch. In my case, I used useRouteMatch to know the previous path and this is a case when useLocation becomes the alternative solution.

### v5
```javascript
    const BookItem = (props) => {
      const itemCtx = useContext(ItemContext);
      const match = useRouteMatch();
      const updateItemHandler = () => {
        itemCtx.updateItem(props.item);
      };
      return (
        <Fragment>
          <Link
            className={classes.box}
            to={`${match.path}/${props.item.id}`}
            onClick={updateItemHandler}
          >
            <img
              className={classes.image}
              src={props.item.image ? props.item.image : noImage}
              alt={props.item.title}
            ></img>
          </Link>
        </Fragment>
      );
    };
```
### v6
```javascript
    const BookItem = (props) => {
      const itemCtx = useContext(ItemContext);
      const location = useLocation();
      const updateItemHandler = () => {
        itemCtx.updateItem(props.item);
      };
      return (
        <Fragment>
          <Link
            className={classes.box}
            to={`${location.pathname}/${props.item.id}`}
            onClick={updateItemHandler}
          >
            <img
              className={classes.image}
              src={props.item.image ? props.item.image : noImage}
              alt={props.item.title}
            ></img>
          </Link>
        </Fragment>
      );
    };
```
## useHistory -> useNavigate

useHistory is no longer available and use useNavigate instead when you want to navigate from the previous page to another page.

### v5
```javascript
    const history = useHistory();
    const submitHandler = (event) => {
        event.preventDefault();
        const formattedDate = formatDate(inputDate);
        const mybook = {
          id: "" + inputDate.getTime() + itemCtx.item.id,
          date: formattedDate,
          rating,
          title: itemCtx.item.title,
          authors: itemCtx.item.authors,
          image: itemCtx.item.image,
          comment,
        };

    myBooksCtx.updateMyBooks(mybook);
        history.replace("/my-books");
      };
```
### v6

Put an object to specify when you want to replace.
```javascript
    const navigate = useNavigate();
    const submitHandler = (event) => {
        event.preventDefault();
        const formattedDate = formatDate(inputDate);
        const mybook = {
          id: "" + inputDate.getTime() + itemCtx.item.id,
          date: formattedDate,
          rating,
          title: itemCtx.item.title,
          authors: itemCtx.item.authors,
          image: itemCtx.item.image,
          comment,
        };

    myBooksCtx.updateMyBooks(mybook);
        navigate("/my-books", { replace: true });
      };
```
## activeClassName -> isActive

activeClassName was deleted and we have to recognize if a certain nav is active or not by using isActive instead.

### v5
```javascript
    const MainNavigation = () => {
      return (
        <header className={classes.header}>
          <div className={classes.logo}>Your Library</div>
          <nav className={classes.nav}>
            <ul>
              <li>
                <NavLink to="/search-book" exact activeClassName={classes.active}>
                  Home
                </NavLink>
              </li>
              <li>
                <NavLink to="/my-books" activeClassName={classes.active}>
                  My Books
                </NavLink>
              </li>
              <li>
                <NavLink to="/" exact activeClassName={classes.active}>
                  Logout
                </NavLink>
              </li>
            </ul>
          </nav>
        </header>
      );
    }
```
### v6
```javascript
    const MainNavigation = () => {
      return (
        <header className={classes.header}>
          <div className={classes.logo}>Your Library</div>
          <nav className={classes.nav}>
            <ul>
              <li>
                <NavLink
                  to="/search-book"
                  className={({ isActive }) => (isActive ? classes.active : null)}
                >
                  Home
                </NavLink>
              </li>
              <li>
                <NavLink
                  to="/my-books"
                  className={({ isActive }) => (isActive ? classes.active : null)}
                >
                  My Books
                </NavLink>
              </li>
              <li>
                <NavLink
                  to="/"
                  className={({ isActive }) => (isActive ? classes.active : null)}
                >
                  Logout
                </NavLink>
              </li>
            </ul>
          </nav>
        </header>
      );
    };
```
## Redirect -> Navigate

Although I did not use Redirect, I think the deletion of Redirect is another big change from v5. Alternative Component is Navigate but attributes are bit changed. In Redirect, replace was the default and we used push if necessary. On the other hand, in Navigate, push is the default and we need to use replace.

### v5
```javascript
    <Redirect to="contactme" />
    <Redirect to="home" push />
```
### v6
```javascript
    <Navigate to="contactme" replace />
    <Navigate to="home" />
```
You can find more detailed information from [the official document](https://reactrouter.com/docs/en/v6/upgrading/v5).

My impression of this update is that it becomes easier to deal with nested paths in v6. Also, it gets more important to separate page components and other components because of using element, which I believe makes my coding cleaner. However, the deletion of activeClassName is a bit inconvenient for me.

Thank you for reading :)

The original article is [here](https://medium.com/@lada496/react-router-updating-from-v5-89c87a65b630)