32
loading...
This website collects cookies to deliver better user experience
index.html
file made available in the public
folder. If you create anchor tag links expecting your users to be navigated to another landing URL, it simply will not work as the only .html
page exported from the build at this stage is the root file./
,/product-category/products
,/product-category/products/ABC
-> /product-category/products/:productId
-> console.log(productId) // "ABC",/typographies
/colour-palette
/buttons
/
/ui/*
/components/*
/feedback
/ui/typographies
/ui/colour-palette
/ui/buttons
react-router-dom
library to get started:npm install react-router-dom
npm install --save-dev @types/react-router-dom
Router
contextual information can propagate down to your components.// src/App.tsx
import { BrowserRouter } from "react-router-dom";
import { CssBaseline, ThemeProvider } from "@material-ui/core";
import AppBar from "./components/AppBar";
import BodyContent from "./components/BodyContent";
import Routes from "./Routes";
import Theme from "./theme";
export default function App() {
return (
<ThemeProvider theme={Theme}>
<CssBaseline />
<BrowserRouter>
<AppBar />
<BodyContent>
<Routes />
</BodyContent>
</BrowserRouter>
</ThemeProvider>
);
}
BrowserRouter
component wraps your content.// src/components/BodyContent/index.tsx
import React from "react";
import { makeStyles } from "@material-ui/core";
const useStyles = makeStyles(() => ({
root: {
margin: '0 auto',
maxWidth: '57rem',
padding: '2rem 0'
}
}))
export default function BodyContent({ children }: { children: React.ReactNode }) {
const classes = useStyles();
return (
<main className={classes.root}>
{children}
</main>
)
}
// src/Routes.tsx
import React from "react";
import { Route, Switch } from "react-router-dom";
import Buttons from "./ui/Buttons";
import ColourPalette from "./ui/ColourPalette";
import Typographies from "./ui/Typographies";
export default function Routes() {
return (
<Switch>
<Route path="/ui/buttons" component={Buttons} />
<Route path="/ui/colour-palette" component={ColourPalette} />
<Route path="/ui/typographies" component={Typographies} />
</Switch>
);
}
Route
and Switch
.The Route component is perhaps the most important component in React Router to understand and learn to use well. Its most basic responsibility is to render some UI when its path matches the current URL.
Renders the first child <Route>
or <Redirect>
that matches the location.
How is this different than just using a bunch of <Route>
s?
<Switch>
is unique in that it renders a route exclusively. In contrast, every <Route>
that matches the location renders inclusively.
// src/components/MainMenu/index.tsx
import React from "react";
import { useHistory } from "react-router";
import { Drawer, List, ListItem, ListItemText } from "@material-ui/core";
const menuItems = [
{ label: 'Buttons', url: '/ui/buttons' },
{ label: 'Colour Palette', url: '/ui/colour-palette' },
{ label: 'Typogaphies', url: '/ui/typographies' },
]
function MenuItems({ setOpenMenu }: { setOpenMenu: React.Dispatch<React.SetStateAction<boolean>> }) {
const { push } = useHistory();
const onLinkNavigation = (url: string) => {
push(url);
setOpenMenu(false);
}
return (
<List>
{menuItems.map(({ label, url }) => (
<ListItem button key={label} onClick={() => onLinkNavigation(url)}>
<ListItemText primary={label} />
</ListItem>
))}
</List>
)
}
/* ...Rest of code */
menuItems
outside the component, this is simply to initialize the menuItems once and refer to it there after.History
hook and explicitly require its push
function for future use.onLinkNavigation
to manage the users click event. Upon clicking we instruct the App to push the new navigation URL into the browsers History queue; then we hide the menu.react-router-dom
Link
component; which would ultimately render our ListItem
component as an anchor tag, with a href value.// src/components/MainMenu/index.tsx
import React from "react";
import { Link } from "react-router-dom";
import { Drawer, List, ListItem, ListItemText } from "@material-ui/core";
const menuItems = [
{ label: 'Buttons', url: '/ui/buttons' },
{ label: 'Colour Palette', url: '/ui/colour-palette' },
{ label: 'Typogaphies', url: '/ui/typographies' },
]
function MenuItems({ setOpenMenu }: { setOpenMenu: React.Dispatch<React.SetStateAction<boolean>> }) {
return (
<List>
{menuItems.map(({ label, url }) => (
<ListItem
button
component={Link}
key={label}
onClick={() => setOpenMenu(false)}
to={url}
>
<ListItemText primary={label} />
</ListItem>
))}
</List>
)
}
/* ...Rest of code */
Link
component from react-router-dom
and then passed it through to the ListItem
"component" property. This then extends the TypeScript definition of ListItem
with the types of Link
, making the "to" property available.History
hooks as we've passed the menuItem's url value into the "to" property./productCategory/:category/product/:productId
const { match: { params }} = useParams();
console.log(params);
// { category: string?, productId: string? }
const { search } = useLocation();
console.log(search);
// ""
/products-page?category=CATEGORY_ID&productId=PRODUCT_ID
const { search } = useLocation();
console.log(search);
// "?category=CATEGORY_ID&productId=PRODUCT_ID"
const { match: { params }} = useParams();
console.log(params);
// {}
/productCategory/:category/product/:productId?tab=general
const { match: { params }} = useParams();
console.log(params);
// { category: string?, productId: string? }
const { search } = useLocation();
console.log(search);
// "?tab=general"
import React from "react";
import { Route, RouteComponentProps, Switch } from "react-router-dom";
export default function Routes() {
return (
<Switch>
<Route path="/ui/:name" component={UIPage} />
</Switch>
);
}
function UIPage({ match: { params: { name } } }: RouteComponentProps<{ name?: string }>) {
return (
<>
name: {name}
</>
)
}
/ui/
= parent route. :name
= parameter name.UIPage
component so you can see how the parent Route
component propagates data down.RouteComponentProps
definition so our codebase has reference to it.BrowserRouter
.import React from "react";
import { Route, RouteComponentProps, Switch, useRouteMatch } from "react-router-dom";
export default function Routes() {
return (
<Switch>
<Route path="/ui/:name" component={UIPage} />
</Switch>
);
}
function UIPage({ match: { params: { name } } }: RouteComponentProps<{ name?: string }>) {
return (
<>
name: {name}
<Child1 />
</>
)
}
function Child1() {
return <Child2 />
}
function Child2() {
return <Child3 />
}
function Child3() {
const { params } = useRouteMatch();
return (
<>
<br />
URL parameter: {JSON.stringify(params)}
</>
)
}
useRouteMatch
hook which exposes the route's current Match properties. The component now has access to the URL parameter to do as it wishes.// src/components/MainMenu/index.tsx
import React from "react";
import { Link, useLocation } from "react-router-dom";
import { Drawer, List, ListItem, ListItemText } from "@material-ui/core";
const menuItems = [
{ label: 'Buttons', url: '/ui/buttons' },
{ label: 'Colour Palette', url: '/ui/colour-palette' },
{ label: 'Typogaphies', url: '/ui/typographies' },
]
function MenuItems({ setOpenMenu }: { setOpenMenu: React.Dispatch<React.SetStateAction<boolean>> }) {
const { pathname } = useLocation();
return (
<List>
{menuItems.map(({ label, url }) => (
<ListItem
button
component={Link}
key={label}
onClick={() => setOpenMenu(false)}
style={pathname === url ? { backgroundColor: '#40bfb4' } : undefined}
to={url}
>
<ListItemText primary={label} />
</ListItem>
))}
</List>
)
}
/* ...Rest of code */
useLocation
hook so we can use the pathname
to validate if one of our links are activestyle
prop to the ListItem
component so we can visually change the background colour if it is active.useHistory
and useLocation
expose it. But the truth of the matter is useLocation
is the one to use in this case as it exposes the current state values.import React from "react";
import { Redirect, Route, RouteComponentProps, Switch, useRouteMatch } from "react-router-dom";
export default function Routes() {
return (
<Switch>
<Redirect from="/ui/:name" to="/uiNew/:name" />
<Route path="/uiNew/:name" component={UIPage} />
</Switch>
);
}
/* ...Rest of code */
Redirect
component before the Route
Componentfrom
prop with the old URL we want to redirect from. Likewise we're defined the to
prop to instruct where to redirect to.Route
to contain the new pathname and the rest is business as usual.