React Router 2019: Routing with Hooks

Published on 7.7.2019

React Router is one of the most popular routing libraries in the React ecosystem. As React continues to evolve, the introduction of hooks has transformed how we write components. In this post, we’ll explore how to use React Router in a hook-based environment and demonstrate how to implement authentication redirects to enhance user experience and security.


React Router: A Brief Overview

React Router allows you to handle routing in your React applications. It provides navigation between views and keeps your application’s URL in sync with the UI. Until React introduced hooks, React Router was typically used with class components and lifecycle methods. But with React’s new functional components and hooks, managing routes in a functional way became much more efficient and declarative.

React Router has evolved to support hooks, making it easier to manage routing and URL changes in functional components. In this tutorial, we’ll look at how to leverage these hooks in your app to handle routing more effectively.


Setting Up React Router with Hooks

First, let’s set up React Router in your project. If you haven’t installed it yet, you can do so by running:

npm install react-router-dom

Once installed, you can begin using React Router in your app. Here’s a simple setup for routing with hooks:

import React from 'react';
import { BrowserRouter as Router, Route, Switch, Link } from 'react-router-dom';

const Home = () => <h2>Home</h2>;
const About = () => <h2>About</h2>;
const Contact = () => <h2>Contact</h2>;

function App() {
  return (
    <Router>
      <div>
        <nav>
          <ul>
            <li>
              <Link to="/">Home</Link>
            </li>
            <li>
              <Link to="/about">About</Link>
            </li>
            <li>
              <Link to="/contact">Contact</Link>
            </li>
          </ul>
        </nav>

        <Switch>
          <Route path="/" exact component={Home} />
          <Route path="/about" component={About} />
          <Route path="/contact" component={Contact} />
        </Switch>
      </div>
    </Router>
  );
}

export default App;

Here’s what’s going on in the code:

  • BrowserRouter (renamed as Router for convenience) is used as the router provider for your app.
  • The Link component is used to navigate between routes, replacing traditional anchor (<a>) tags with React’s link system.
  • Route components are used to match specific paths and render corresponding components.

In this setup, when a user clicks on one of the navigation links, the URL will change, and the corresponding component will be displayed.


Using React Router Hooks

React Router has several hooks that make it easy to interact with the router inside your functional components. Here are some of the most important hooks you can use in your application:

  1. useHistory – Provides access to the history object, which allows you to programmatically navigate to different routes.
  2. useLocation – Returns the current location object, which contains information about the current URL, including pathname, search, and hash.
  3. useParams – Retrieves dynamic parameters from the current URL (useful in route patterns like /user/:id).
  4. useRouteMatch – Matches the current location to a route pattern.

Let’s walk through how to use some of these hooks in practice:

Using useHistory to Programmatically Navigate

With useHistory, you can navigate programmatically in your app. For example, you can redirect users after a successful login or form submission:

import React, { useState } from 'react';
import { useHistory } from 'react-router-dom';

function Login() {
  const [username, setUsername] = useState('');
  const [password, setPassword] = useState('');
  const history = useHistory();

  const handleLogin = () => {
    // Assume successful login
    console.log('User logged in:', username);
    // Redirect to home page
    history.push('/');
  };

  return (
    <div>
      <h2>Login</h2>
      <input
        type="text"
        placeholder="Username"
        value={username}
        onChange={(e) => setUsername(e.target.value)}
      />
      <input
        type="password"
        placeholder="Password"
        value={password}
        onChange={(e) => setPassword(e.target.value)}
      />
      <button onClick={handleLogin}>Login</button>
    </div>
  );
}

export default Login;

In this example:

  • After the user successfully logs in, the history.push('/') method will navigate to the home page (/).

Using useLocation to Access Current URL

The useLocation hook gives you access to the current URL, which can be useful for things like determining the active route or implementing conditional rendering based on the location:

import React from 'react';
import { useLocation } from 'react-router-dom';

function CurrentPage() {
  const location = useLocation();

  return <p>Current path: {location.pathname}</p>;
}

export default CurrentPage;

Here, useLocation provides an object containing details about the current URL, and we’re simply displaying the pathname in the UI.


Implementing Authentication Redirects

A common use case in many React apps is redirecting users to a login page or a specific route after authentication. In this section, we’ll cover how to implement authentication redirects using React Router and the useHistory hook.

Redirecting Based on Authentication Status

Let’s say we have a simple app where the user is either logged in or logged out. We want to redirect them to the login page if they try to access a restricted route without being authenticated.

First, let’s create a mock authentication context:

import React, { createContext, useContext, useState } from 'react';

const AuthContext = createContext();

export function useAuth() {
  return useContext(AuthContext);
}

export function AuthProvider({ children }) {
  const [isAuthenticated, setIsAuthenticated] = useState(false);

  const login = () => setIsAuthenticated(true);
  const logout = () => setIsAuthenticated(false);

  return (
    <AuthContext.Provider value={{ isAuthenticated, login, logout }}>
      {children}
    </AuthContext.Provider>
  );
}

This AuthProvider component uses React’s Context API to manage the authentication state, and the useAuth hook allows us to access the authentication state throughout the app.

Now, let’s implement a protected route and a login page:

Protected Route Component

import React from 'react';
import { Redirect, Route } from 'react-router-dom';
import { useAuth } from './AuthProvider';

function ProtectedRoute({ component: Component, ...rest }) {
  const { isAuthenticated } = useAuth();

  return (
    <Route
      {...rest}
      render={(props) =>
        isAuthenticated ? (
          <Component {...props} />
        ) : (
          <Redirect to="/login" />
        )
      }
    />
  );
}

export default ProtectedRoute;

The ProtectedRoute component checks if the user is authenticated. If they are, it renders the passed-in component; if not, it redirects them to the login page.

Login Page

import React from 'react';
import { useHistory } from 'react-router-dom';
import { useAuth } from './AuthProvider';

function Login() {
  const history = useHistory();
  const { login } = useAuth();

  const handleLogin = () => {
    login();
    history.push('/protected');
  };

  return (
    <div>
      <h2>Login</h2>
      <button onClick={handleLogin}>Login</button>
    </div>
  );
}

export default Login;

When the user clicks the login button, we call login() to update the authentication state, then use history.push() to redirect the user to the protected route.


Conclusion

In this post, we’ve learned how to use React Router with hooks, which makes routing in functional components easier and more intuitive. We also covered how to implement authentication redirects to protect routes and ensure that only authenticated users can access certain parts of the application.

By using hooks like useHistoryuseLocation, and useParams, you can create dynamic and efficient routing solutions in your React app. This hook-based approach aligns with the modern React paradigm and offers a simpler way to manage routing without the need for class components or lifecycle methods.

With this knowledge, you can build more efficient, secure, and user-friendly applications that leverage the power of React Router and hooks.

Happy coding!

Comments

No comments yet. Why don’t you start the discussion?

Leave a Reply

Your email address will not be published. Required fields are marked *