In this chapter, we will learn about the basics of some components and hooks
provided by the react-router-dom library. We will be using the v5 version of
the library in this book. It's worth mentioning that the latest version, v6,
which is the default installation, features functional differences in some
components compared to the previous version.
BrowserRouter component
The BrowserRouter is a router
implementation that uses HTML5 history API to keep the UI in sync with the URL.
It is the parent component used to store all the other components.
Usually, BrowserRouter is used in the main component or at the entry point of
the application. All the other components are added as the children for the
BrowserRouter component. The BrowserRouter keeps the UI in sync for all the
children components. If we want to use routing in a component then that
component should be a child of BrowserRouter component. When we try to use
react-router-dom components like Route, etc outside the BrowserRouter then
we will get an error mentioning "You should not use <Route> outside a <Router>",
like this:
We should have only one BrowserRouter at the topmost level of the component
hierarchy tree with all the other components nested inside it. We should have a
single source of truth for updating the browser history. Each BrowserRouter
has its own history stack. If we use multiple BrowserRouter then the Routes
in the parent BrowserRouter will not work from the child BrowserRouter
component.
For example, let's say we have three components called App.jsx, About.jsx,
and Contact.jsx. We have defined a BrowserRouter component in App.jsx like
so:
So, as mentioned above we have two Route for paths /about and /contact.
About component will be rendered for the /about path and Contact component
will be rendered for the /contact path.
Now, let's say that in the About component we use another BrowserRouter and
define a Link for Contact component, like this:
Now when we click on the To contact page link defined in the About component
it will not work because for the BrowserRouter defined in the About
component we don't have any route defined for the /contact path. Hence, we
should use only one BrowserRouter at the entry point of the application.
Switch component
The Switch component renders the first matching child <Route> or
<Redirect>. The purpose of using Switch component is to render only one
matching <Route> or <Redirect> at a time.
For example, let's say we have defined some routes for About, User, and
NoMatch components, like this:
Now, if we go to /about path then all three components that are About,
User, and NoMatch will render because they all match the current location's
path. If we want to render only one component for the matching path then we
should use the Switch component, like this:
Now only one component will be rendered for the matching path. After using
Switch component, if we go to /about path then the first matching route that
is About component will be rendered.
There are a few scenarios where it may not be appropriate to use routes within a
Switch component in React:
-
If you do not need to display different components based on the URL path, then
using routes within a Switch may be unnecessary. For example, if you have a
simple application that only needs to display a single component, you can
avoid using routes and simply render the component directly.
-
Consider you need to display the same component for multiple URL paths. Using
multiple Route components within a Switch for the same component can cause
unnecessary rendering and may lead to performance issues.
The Switch component renders the component for the first matching route. So,
if there are multiple matches then the order in which the routes are defined
matters. To solve this problem, we should define the common or basic routes at
the end of the Switch component or we can use the exact prop. We have
discussed the exact prop in the next section.
Route component and associated props
The Route component is used to define a path that a user can navigate to and
the corresponding component that should be displayed when the path is matched.
It is typically used within a BrowserRouter component, which manages the
rendering of the components based on the current URL.
The usage of Route is straightforward.
In this example, the BrowserRouter component has three Route components as
children, each with a different path prop. When the user navigates to one of
these paths, the corresponding component will be displayed.
The Route component also has several other props that you can use to customize
its behavior. We will discover each of them in the coming sections.
exact prop
We can add the exact prop in the Route component so that it will match only
if paths are exactly the same.
For example, let's consider that we have two components Home and Dashboard
and we have defined the routes like this:
Now if we go to "/dashboard" path then Home component will be rendered.
Because "/" pattern is matching with the "/dashboard" path and it is defined
above the Dashboard route. So the first matching route is for the Home
component. There are two solutions to this problem. We can move the Dashboard
component route above the Home component route or we can use the exact prop in
the Home component route.
We can solve the above-mentioned problem using the exact prop like this:
Now, the Home component will be rendered only if there is an exact match with
the current location's path.
We should not use the exact prop in the routes if there are nested routes.
For example, let's say we have defined the routes like this:
And in the Dashboard component we have defined the routes like this:
Now if we go to the /dashboard/projects path then first the Dashboard
component will render and from the Dashboard component the /projects route
will be matched and the Projects component will be rendered. If we had used
the exact prop in the /dashboard route then the /dashboard/projects will
not match with the /dashboard path and nested routes will not work. So, we can
not use the exact prop if there are nested routes.
component vs render prop
The component and render are the props used in Route component. Both of
them help in rendering a component or JSX when the current location's path
matches.
For example:
But how can we pass some props into this Dashboard component?
You might be tempted to pass the props to an inline function within the
component prop of the Route, like shown below:
But if we pass an inline function to the component prop, then it would create
a new component on every render. This results in unmounting the existing
component and mounting the new component instead of just updating the existing
one. So this approach can be inefficient and will cause performance issues.
For passing custom props in the Dashboard component we can use the render
prop which accepts the inline functions for rendering, like this:
So, while adding routes if we want to render a component without any custom
props then we should use the component prop and if we want to use an inline
function or want to pass some custom props in the component then we should use
the render prop.
Link Component
The Link component in React Router is used to create a link to a specific
route within your application. It is similar to an a element in HTML, but
instead of navigating to a new page, it updates the URL and renders the
corresponding component for the new route within the same page.
Here's an example of how you might use the Link component in a React
application:
In this example, the Link components are used to create navigation links within
the application. When a user clicks on one of these links, the URL will be
updated and the corresponding component will be displayed.
The Link component has several props that you can use to customize its behavior,
such as the to prop, which specifies the route to link to, and the replace
prop, which specifies whether the new route should replace the current one in
the history stack.
Redirect component
The Redirect component in React Router is used to redirect the user to a
different route within your application. It is typically used when you want to
change the route based on some condition, such as when a form submission is
successful or when the user is not authenticated.
Here's an example of how you might use the Redirect component in a React
application:
In this example, the Redirect component is rendered if the isLoggedIn state
is true. When this happens, the user is redirected to the /dashboard route.
The Redirect component has several props that you can use to customize its
behavior, such as the to prop, which specifies the route to redirect to, and
the push prop, which specifies whether the new route should be added to the
history stack or replace the current one.
Some react-router hooks
The useHistory hook gives us access to the history instance that we can use to
navigate between pages. We can push the required path in the history instance
and the required page will be loaded by the router.
Let us see an example:
In the above example, when a user clicks on the button labeled "Go to Home", the
page corresponding to the path /home will be rendered.
windows.location.href is an alternative to history.push that developers
often tend to use or let's say "abuse". window.location.href is a property of
the location object that represents the current URL of the page. You can use it
to get the current URL or to navigate to a new URL. However, using
window.location.href to navigate to a new URL will cause the browser to reload
the page, which can be slower and less efficient than using history.push.
It is generally recommended to use history.push to navigate within a React
app, especially if you are using react-router-dom to handle routing. However,
there may be cases where using window.location.href is necessary, such as when
you need to open a new tab or window, or when you need to navigate to a URL that
is outside of your React app.
It is also common for developers to use both history.replace and
history.push methods in an application. However, it is crucial to understand
that these methods cannot be used synonymously. The router history in a web
application functions as a stack of routes. When history.replace is used, the
current top of the stack is overwritten with the new route, while history.push
adds a new route to the top of the stack. For instance, history.replace can be
employed when navigating from an invalid route to prevent the user from
navigating back to the invalid page. Hence, understanding the differences
between these two methods is essential in effectively manipulating the browser's
history stack in a web application.
The useLocation hook returns the location object that represents the current
URL. The location object consists of the following properties:
hash — (string) The URL hash (#) fragment.
pathname — (string) The path of the URL.
search — (string) The URL query string (?).
Consider a URL like https://example.com/path?key1=value1&key2=value2#fragment.
In this example, the URL has a path of /path, a query string of
key1=value1&key2=value2, and a hash fragment of fragment.
We can think about the useLocation hook as a useState that returns a new
location whenever the URL changes.
For example:
As you might have expected, whenever the URL changes, "URL changed" will be
logged into your browser.
The useParams hook gives us an object of key-value pairs of URL parameters.
Using the useParams hook we can access the parameter of the current route,
like this:
Here the current article slug will be stored in the variable slug.
The useRouteMatch hook attempts to match the current URL in the same way that
a Route would. It will contain the following properties:
isExact — (boolean) Check if the entire URL was matched.
params — (object) Key/values pairs parsed from the URL.
path — (string) The path pattern used to match.
url — (string) The matched portion of the URL.
We should use the useRouteMatch when we need the match data without actually
rendering a Route.
For example:
Here match.params.slug provides the value for slug from the matched object.
In summary, React Router makes it easy to add routing to your React application,
allowing you to create complex, nested routing configurations with declarative,
component-based code. For more information on using React Router v5, you can
refer to the
official documentation