REACT ROUTING - rs-hash/Senior GitHub Wiki

BASICS

https://blog.webdevsimplified.com/2022-07/react-router/

  • run npm i react-router-dom to install React Router.
  • Setup your router
  • Define your routes
  • Handle navigation

Routers

  • BrowserRouter - mostly we use
  • HashRouter

This router works very similarly to the BrowserRouter, but the main difference is that instead of changing the URL to something like http://localhost:3000/books it will store the URL in the hash like so http://localhost:3000/#/books. As you can see this URL has a # after the URL which represents the hash portion of the URL. Anything in the hash portion of the URL is just additional information that usually denotes an id on the page for scrolling purposes since a page will automatically scroll to the element with the id represented by the hash when the page loads.

In React Router this hash is not actually used to store id information for scrolling, but instead it stores information related to the current URL. The reason React Router does this is because some hosting providers do not allow you to actually change the URL of your page. In those very rare circumstances you will want to use the HashRouter since the HashRouter will not change the actual URL of your page and will only change the hash of your page. If you are able to use any URL with your hosting provider then this is not something you should use.

  • HistoryRouter

is a router that allows you to manually control the history object that React Router uses to store all the information related to the history of your application’s routing. This history object helps make sure things like the back and forward button in the browser work properly.

  • MemoryRouter ( Testing that dont connect to browser )

MemoryRouter on the other hand stores all its information in memory which means it never accesses the browser and is ideal when trying to unit test components. Other than this specific use case, though, this router is never to be used.

  • StaticRouter( Server )

The final router is the StaticRouter and this router again has a very specific use case. This router is specifically meant for server rendering your React applications since it takes in a single location prop and renders out your application using that location prop as the URL. This router cannot actually do any routing and will just render a single static page, but that is perfect for server rendering since you want to just render the HTML of your application on the server and then the client can set up all your routing and so on.

  • NativeRouter - React Router Native

Configuring The Router

import React from "react"
import ReactDOM from "react-dom/client"
import App from "./App"
import { BrowserRouter } from "react-router-dom"

const root = ReactDOM.createRoot(document.getElementById("root"))
root.render(
  <React.StrictMode>
    <BrowserRouter>
      <App />
    </BrowserRouter>
  </React.StrictMode>
)

Defining Routes

import { Route, Routes } from "react-router-dom"
import { Home } from "./Home"
import { BookList } from "./BookList"

export function App() {
  return (
    <Routes>
      <Route path="/" element={<Home />} />
      <Route path="/books" element={<BookList />} />
    </Routes>
  )
}

Handling Navigation

  • This Link component is just a wrapper around an anchor tag that helps ensure all the routing and conditional re-rendering is handled properly so you can use it just like your would a normal anchor tag.
import { Route, Routes, Link } from "react-router-dom"
import { Home } from "./Home"
import { BookList } from "./BookList"

export function App() {
  return (
    <>
      <nav>
        <ul>
          <li>
            <Link to="/">Home</Link>
          </li>
          <li>
            <Link to="/books">Books</Link>
          </li>
        </ul>
      </nav>

      <Routes>
        <Route path="/" element={<Home />} />
        <Route path="/books" element={<BookList />} />
      </Routes>
    </>
  )
}

Dynamic Routing

Router Specificity

  • If you have a dynamic route and hardcoded route together, Router V6 will be able to identify differenc between dynamic and static - it will choose hardcoded route
<Routes>
  <Route path="/" element={<Home />} />
  <Route path="/books" element={<BookList />} />
  <Route path="/books/:id" element={<Book />} />
</Routes>
import { useParams } from "react-router-dom"

export function Book() {
  const { id } = useParams()

  return <h1>Book {id}</h1>
}

Nested Route

<Routes>
  <Route path="/" element={<Home />} />
  <Route path="/books">
    <Route index element={<BookList />} />
    <Route path=":id" element={<Book />} />
    <Route path="new" element={<NewBook />} />
  </Route>
  <Route path="*" element={<NotFound />} />
</Routes>

Shared Layout

<Routes>
  <Route path="/" element={<Home />} />
  <Route path="/books" element={<BooksLayout />}>
    <Route index element={<BookList />} />
    <Route path=":id" element={<Book />} />
    <Route path="new" element={<NewBook />} />
  </Route>
  <Route path="*" element={<NotFound />} />
</Routes>

import { Link, Outlet } from "react-router-dom"

/* BooksLayout */
export function BooksLayout() {
  return (
    <>
      <nav>
        <ul>
          <li>
            <Link to="/books/1">Book 1</Link>
          </li>
          <li>
            <Link to="/books/2">Book 2</Link>
          </li>
          <li>
            <Link to="/books/new">New Book</Link>
          </li>
        </ul>
      </nav>

      <Outlet />
    </>
  )
}
  • The way our new code will work is whenever we match a route inside the /book parent Route it will render the BooksLayout component which contains our shared navigation.
  • Then whichever child Route is matched will be rendered wherever the Outlet component is placed inside our layout component.
  • The Outlet component is essentially a placeholder component that will render whatever our current page’s content is.
  • This structure is incredibly useful and makes sharing code between routes incredibly easy.

Outlet Context

import { Link, Outlet } from "react-router-dom"

export function BooksLayout() {
  return (
    <>
      <nav>
        <ul>
          <li>
            <Link to="/books/1">Book 1</Link>
          </li>
          <li>
            <Link to="/books/2">Book 2</Link>
          </li>
          <li>
            <Link to="/books/new">New Book</Link>
          </li>
        </ul>
      </nav>

      <Outlet context={{ hello: "world" }} />
    </>
  )
}


import { useParams, useOutletContext } from "react-router-dom"

export function Book() {
  const { id } = useParams()
  const context = useOutletContext()

  return (
    <h1>
      Book {id} {context.hello}
    </h1>
  )
}

Nested Routes

<Routes>
  <Route path="/" element={<Home />} />
  <Route path="/books/*" element={<BookRoutes />} />
  <Route path="*" element={<NotFound />} />
</Routes>

import { Routes, Route } from "react-router-dom"
import { BookList } from "./pages/BookList"
import { Book } from "./pages/Book"
import { NewBook } from "./pages/NewBook"
import { BookLayout } from "./BookLayout"

export function BookRoutes() {
  return (
    <Routes>
      <Route element={<BookLayout />}>
        <Route index element={<BookList />} />
        <Route path=":id" element={<Book />} />
        <Route path="new" element={<NewBook />} />
        <Route path="*" element={<NotFound />} />
      </Route>
    </Routes>
  )
}

useRoutes Hook

import { Route, Routes } from "react-router-dom"
import { Home } from "./Home"
import { BookList } from "./BookList"
import { Book } from "./Book"

export function App() {
  const element = useRoutes([
    {
      path: "/",
      element: <Home />,
    },
    {
      path: "/books",
      children: [
        { index: true, element: <BookList /> },
        { path: ":id", element: <Book /> },
      ],
    },
  ])

  return element
}

Handling Navigation

Link Navigation

Props

  • to
  • replace - The replace prop is a boolean that when set to true will cause this link to replace the current page in the browser history.The page your were currently on was replaced with the new page
  • reloadDocument - f it is set to true your Link component will act like a normal anchor tag and do a full page refresh on navigation instead of just re-rendering the content inside your Routes component.
  • state -The final prop is called state. This prop lets you pass data along with your Link that does not show up anywhere in the URL.

NavLink

  • This component works exactly the same as the Link component, but it is specifically for showing active states on links, for example in nav bars
<NavLink
  to="/"
  style={({ isActive }) => ({ color: isActive ? "red" : "black" })}
>
  Home
</NavLink>

Manual Navigation

<Navigation /> Component

  • Now sometimes you want to manually navigate a user based on things like submitting a form or not having access to a specific page. For those use cases you will need to either use the Navigate component or the useNavigation hook.
  • has same props as Link
<Navigate to="/" />

useNavigation Hook

  • This hook is a really simple hook that takes no parameters and returns a single navigate function which you can use to redirect a user to specific pages.
  • This navigate function takes two parameters.
  • The first parameter is the to location you want to redirect the user to and the second parameter is an object that can have keys for replace, and state.
const navigate = useNavigate()

function onSubmit() {
  // Submit form results
  navigate("/books", { replace: true, state: { bookName: "Fake Title" } })
}

navigate(-1) // Go back one page in history
navigate(-3) // Go back three pages in history
navigate(1) // Go forward one page in history

Navigation Data

There are 3 main ways you can pass data between pages.

  • Dynamic Parameters - using the useParams hook. This is the best way to handle passing information like ids.
  • Search Parameters
  • State/Location Data

Search Parameters

  • Search parameters are all of the parameters that come after the ? in a URL (?name=Kyle&age=27)
  • In order to work with search parameters you need to use the useSearchParams hook which works very similarly to the useState hook.
import { useSearchParams } from "react-router-dom"

export function SearchExample() {
  const [searchParams, setSearchParams] = useSearchParams({ n: 3 })
  const number = searchParams.get("n")

  return (
    <>
      <h1>{number}</h1>
      <input
        type="number"
        value={number}
        onChange={e => setSearchParams({ n: e.target.value })}
      />
    </>
  )
}

State/Location Data

const location = useLocation()

If we have the following URL http://localhost/books?n=32#id then the return value of useLocation would look like this.

{
  pathname: "/books",
  search: "?n=32",
  hash: "#id",
  key: "2JH3G3S",
  state: null
}
⚠️ **GitHub.com Fallback** ⚠️