An online markdown blog and knowledge repository.
There are two versions of React Router:
These notes will be focused on React-Router-Dom, for webapp usage.
npm i react-router-dom
import {BrowserRouter} from 'react-router-dom';
// import statements and root definition...
root.render(
<React.StrictMode>
<BrowserRouter>
<App />
</BrowserRouter>
</React.StrictMode>
)
<Routes>
component and the <Route>
component.// header imports etc...
function App() {
return (
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<AboutUs />} />
</Routes>
)
}
Use a <Link>
component.
Technically an Anchor Tag under the hood.
Instead of using 'href' use to="/"
.
This allows changing Components on a click from a Header-based NavBar, for example.
// imports etc
function App() {
return (
<>
<nav>
<ul>
<li>
<Link to="/">Home</Link>
</li>
<li>
<Link to="/about">About</Link>
</li>
</ul>
</nav>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<AboutUs />} />
</Routes>
</>
)
}
Define routes inside the Routes component.
import { BrowserRouter } from 'react-router-dom'
Most commonly used.
What will be discussed in these notes.
import { HashRouter } from 'react-router-dom'
Instead of storing a URL as a normal URL, it stores a hashed-version of a URL.
A URL that includes a #
hash character is not really a route, but Hash Router allows and manages it properly.
Not recommended by Kyle [of WebDevSimplified], but it exists.
import { unstable_HistoryRouter } from 'react-router-dom'
Enables using Back and Forward routing buttons to navigate the webapp.
Most of the time this is not necessary and it is unstable (currently Aug 2022 version).
import { MemoryRouter } from 'react-router-dom'
Stores everything related to browser clicks in memory.
Does NOT change the URL in the browser.
Useful for testing where a browser is not included in the testing system.
import {StaticRouter} react-router-dom/server
Specify a location attribute to each <StaticRouter location=''>
Component.
Tells the server to render the specific page.
Only use this when doing server-side rendering in React, ensuring the correct page is rendered.
import { NativeRouter } from 'react-router-dom'
Must install 'React Native Router' library.
path="/items/:id"
Use a custom hook 'useParams()' which returns custom object parameters.
'useParams()' is helpful to pull an ID param from a collection of items, which can then be referenced by the Router directly.
<Route path="/items/new" element={<NewItem />} />
The hardcoded path will be selected over the :id
version of the path by default.
If two routes match, then the hardcoded one will be selected by Router, because it is more specific.
This eliminates worry about the order of routing (at least in version 6 - previous versions still must be ordered correctly, top to bottom).
*
<NotFound >
Component (page).// ...inside the <Routes></Routes> tags within the return statement...
<Route path="*" element={<NotFound404 />} />
Make sure this is last in the list.
Related paths should be nested.
Parent path Route needs to be defined, without any element.
Nest child Route tags and specify the element in each one e.g. ...path=':id' element={<PageComponent />} />
Assign an 'index' route to tell Router to use the Parent route as the "Index" page for each child route.
A Parent Route can have an element that points to a Layout as defined in a Component, which will force Router to allow the targeted Layout to be displayed on the routed page.
Think of this as a sub-navigation architecture for a group of pages.
Copy-pasta code to each Component that needs those links? no
Instead, create Layouts that all child components share:
<Link ...>
entries for the sub-navigation that is needed across multiple pages.<h1>blah</h1>
).// wrap the Layout Component around the components within a route definition
// imports etc above this return statement:
return (
<>
<nav>
<ul>
<li>
<Link to="/">Home</Link>
</li>
<li>
<Link to="/about">About</Link>
</li>
</ul>
</nav>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/items" element={<ItemLayout />} >
<Route index element={<Items />} />
<Route path=":id" element={<Item />} />
<Route path="new" element={<NewItem />} />
</Route>
<Route path="/about" element={<AboutUs />} />
<Route path="*" element={<NotFound404 />} />
</Routes>
</>
)
But wait, there's more!
When the Layout renders, Outlet component will display which route (page) is currently selected!
Note: Only child components to the '/items' path will have this child-list of links, per the Routes list!
Simple solution: Remove the 'path=""' attribute and retain only the 'element={}' attribute!
Remember though, if the components do share the same path, do not make this edit.
Also, this technique will support child nesting, as discussed in the previous subsection.
Attribute Context can be added to the Router Outlet component.
// within the return fragment of a Layout Component
<Outlet context= />
// in another component, capture the context as defined in Outlet
const ctxt = useOutletContent();
Works like a React context.
Allows passing down a value from another Component.
The Outlet Component will return a KVP and any Component that uses 'useOutletContext' can capture that data and use it!
This can be useful when there are several components and shared layouts.
Use multiple Routes statement lists!
Example: When a side-bar needs to display a route link that is customized, or duplicated, from the list of links that is defined in the current Layout.
// inside the return fragment...
<Routes>
<Route path="/items" element={<h1>Custom Content</h1>} />
</Routes>
<nav>
// ...items within the nav element...
</nav>
<Routes>
<Route> path="/" element={<Home />} />
<Route path="/items" element={<ItemLayout />}>
// etc...
</Route>
</Routes>
</>
Think of this as being a hard-coded content-specific route.
Separate some routes into a set of routes of their own!
/*
in the parent Path.The 'path/*' matches any routes that follow it, so the existing Routes now looks up the nested Routes.
Use useRoutes: useRoutes([...])
Give an array of objects to useRoutes in javascript:
let el = useRoutes([
{
path: String,
element: <Component />,
children: [
{
index: boolean,
element: <Component />
},
],
}
]);
Now use {el}
to render the js defined routes!
Same effect as using JSX, but using pure javascript instead.
The parameter is an array of objects with arrays of children.
Link to="path" replace: Replaces the history with the path defined in the Link element.
Without replace, history allows the Back button to go back to the previous page.
With replace, history is overwritten and the Back button does not go back to the previous page.
Reloads the entire page instead of just the Router defined section.
Supports using a state={}
statement that erases the URL bar link address.
Multiple properties:
ClassName
Style: Can contain a function.
Children
isActive: The default if not defined.
isActive can be customized e.g.: style={({isActive}) -> { return expression }}
color: red;
import './styles.css'
Parent Link element will remain active when child routes are selected.
To stop this from happening, add attribute end
to the parent route element.
Automatically redirects to another page.
<Navigate to|replace|state />
What about form submission?
Use the useNaviage
hook.
import {Navigate, useNavigate} from 'react-router-dom';
// import statements including useNavigate hook
export function NotFound() {
const navigate = useNavigate();
useEffect(()=> {
setTimeout(()=> {
navigate("/", { })
}, 1000); // milliseconds
}, [])
return <h1>NotFound</h1>
}
The 'navigate' property takes two parameters: to (path) and replace attribute.
'useEffect(arrowFunc)' is the proper way to wrap a function in React here.
Using a -1 instead of a textual path in the navigate() params will cause the browser to go "back" to the previous page (after 1 second per the above code).
In addition to useParams():
?n=10
navigate(path, {state: String})
which will persist data through navigation to the next page that is loadedUse 'state' to show a message based on what page the user was on previously.
Return to conted index