Programmatic navigation refers to when a user is redirected as a result of an action that occurs on a route, like a login or signup action. In this article, we'll look at a myriad of approaches to navigating programmatically with React Router.
The React ideology consists of three core concepts: the user event, state management and render function. Programmatic routing can be said to be in line with this ideology.
The effect of routing programmatically is on the same page as no route changing or, at other times, may bring about a need to change a route. When the need arises, it is not going to be triggered by clicking a link, so we don’t always have to use a Link component, and using a Link component in such scenarios is not optimal.
Sometimes we want a different action: We only want to navigate to a different route when a particular event happens or when a user performs an action like submitting a form that leads you to a new page. We refer to this kind of action as programmatic navigation.
React Router is designed to follow the ideology as mentioned earlier. Thus, programmatically navigating with React Router should, by definition, align with those three core concepts.
React Router provides us with a history object, which is accessible by passing this object into each route as a prop. This history object lets us manually control the history of the browser. Since React Router changes what we see based on the current URL, the history object gives us fine-grained control over when or where individual pieces of the application are shown.
Programmatic Navigation refers to when a user is redirected as a result of an action that occurs on a route. A login or signup action or form submission action on a route is a typical example of navigating programmatically. In this article, we’ll look at a myriad of approaches to navigating programmatically with React Router.
The primary way you programmatically navigate using React Router v4+ is by using a <Redirect />
component, and it’s a recommended method that helps the user navigate between routes.
Using the Redirect component is a different approach but just as valid. The idea is to have it pointing at a state in the component, and if that condition is fulfilled, then navigate.
Some might argue that this method requires more work as one needs to create a new prop on the component’s state and add a condition to the render method to check when to render the Redirect component. This is a fact, but a counter and valid argument, from those who prefer explicit to implicit: It points to the idea that explicitly defining and modifying your state is better as it makes the code more readable against the implicit state handled by an imperative API such as history.push
, which we will go over in a bit.
Here’s a code example of how to use the Redirect component.
Codesandbox: https://codesandbox.io/s/gallant-meitner-bshng?file=/src/App.js
import React, { useState } from 'react';
import { Redirect } from 'react-router-dom';
import { userLogin } from './userAction';
import Form from './Form';
const Login = () => {
const [isLoggedIn, setIsLoggedIn] = useState(false);
const handleLogin = async (userDetail) => {
const success = await userLogin(userDetail);
if(success) setIsLoggedIn(true);
}
if (isLoggedIn) {
return <Redirect to='/profile' />
}
return (
<>
<h1>Login</h1>
<Form onSubmit={handleLogin} />
</>
)
}
export default Login;
history.push() is another approach where we make use of the history
props React Router provides while rendering a component.
In other words, this works when the component is being rendered by React Router, bypassing the component as a Component prop to a Route. If this is the case, the React Router exposes three props to the component: location
, match
and history
.
We’ll focus on the history
prop. The history prop keeps track of all the session history under the hood and provides us with different methods to manipulate it.
The push
method is essential and is used to push a path as a route to the history stack, which executes as Last In First Out (LIFO). This causes the app to redirect to the last route added, thereby redirecting the user to a specified route. The example below assumes the component is rendered with React Router.
Codesandbox: https://codesandbox.io/s/angry-saha-djh3z?file=/src/App.js
import React from "react";
import { userLogin } from "./userAction";
import Form from "./Form";
const Login = props => {
const handleLogin = async userDetail => {
const success = await userLogin(userDetail);
if (success) props.history.push("/profile");
};
return (
<>
<h1>Login</h1>
<Form onSubmit={handleLogin} />
</>
);
};
export default Login;
We mentioned earlier that for a component to have access props.history.push
it must have been rendered with React Router. There are cases where this might not be the case. Thus, we render a component ourselves. To make the history
property available to the component, the React Router team created the Higher Order Component (HOC) withRouter. Wrapping a component with this HOC exposes the properties as well.
Codesandbox: https://codesandbox.io/s/silent-rain-l19lg?file=/src/App.js:0-442
import React from 'react';
import { withRouter } from 'react-router-dom';
import { userLogin } from './userAction';
import Form from './Form';
const Login = (props) => {
const handleLogin = async (userDetail) => {
const success = await userLogin(userDetail);
if(success) props.history.push('/profile');
}
return (
<>
<h1>Login</h1>
<Form onSubmit={handleLogin} />
</>
)
}
export default withRouter(Login);
As of recent versions of React Router (v5.1) and React (v16.8), we have a new method called the useHistory hook which embraces the power of React Hooks. This is used for programmatic navigation purposes within a functional component. The useHistory hook gives you access to the history instance that we can use to navigate between pages, whether the component has been rendered by React Router or not, and this eliminates the need for using withRouter.
Codesandbox: https://codesandbox.io/s/serene-cookies-hc629?file=/src/App.js
import { useHistory } from "react-router-dom";
const HomeButton = () =>{
let history = useHistory();
const handleClick = () => {
history.push("/home");
}
return (
<button type="button" onClick={handleClick}>
Go home
</button>
);
}
export default HomeButton;
The main focus of this article was to share how you can safely navigate between components using the React Router package.
Considering React has a declarative approach to building UIs, using Redirect is the recommended approach for navigation when the Link cannot be used. There is no harm in using the other methods as they are all supported and semantically correct.
Also, with the introduction of useHistory together other other APIs in the 5.1.2 release, it becomes even easier to navigate programmatically as long as you understand how to use React Hooks.
Gift Egwuenu is a developer advocate and technical educator with more than five years of experience in the web development industry. She has a passion for building tools and products that help businesses grow. She enjoys sharing her knowledge and experience through speaking engagements, writing and other educational initiatives. Gift's areas of expertise include web development, JAMstack and career advancement, and she is dedicated to helping others improve their skills and succeed in the tech industry. Gift enjoys reading, cooking and traveling when she is not working.