React Router: Routes, Loaders and Errors

vikram-boominathan

Vikraman

Posted on June 7, 2024

React Router: Routes, Loaders and Errors

React Router is an essential library for managing routing in React applications. It allows you to navigate between different components seamlessly and efficiently. In this comprehensive guide, we will delve into key features of React Router: createBrowserRouter, createRoutesFromElements, loader functions, useLoaderData, and useRouteError. Let's explore these concepts in detail!

createBrowserRouter

The createBrowserRouter function is central to React Router. It sets up the routing system for your application, allowing you to define different routes and their corresponding components. This function takes an array of route objects, each specifying a path and the component to render for that path.

  • Defines routes in a declarative manner, making it clear to manage complex routing structures.
  • Supports data fetching and deferred loading at the route level, allowing to load data as needed for each route.

Router Provider

RouterProvider is a playground where all the routing processes and settings of the React Router are unified. It serves as a facade to the routing capability of our app. We connect our parent component with the RouterProvider to guarantee that each child component has the possibility to use the routing features and can contribute to the routing.

import { createBrowserRouter, RouterProvider } from 'react-router-dom'; 
import HomePage from "./HomePage";
import AboutPage from "./AboutPage";
import MainLayout from "./MainLayout";
import NotFound from "./NotFound";
import Dashboard from "./Dashboard";
import Profile from "./Profile";

const router = createBrowserRouter([
  {
    path: "/",
    element: <HomePage />,
  },
  {
    path: "/about",
    element: <AboutPage />,
  },
  ,
  {
    path: "/main",
    element: <MainLayout />,
    errorElement: <NotFound />,
    children: [
      { path: "dashboard", element: <Dashboard /> },
      { path: "dashboard/profile", element: <Profile /> },
    ],
  },
]);

const App = () => ( <RouterProvider router={router} /> );

export default App

Enter fullscreen mode Exit fullscreen mode
  • The root path ('/') renders the HomePage component.
  • The /about path renders the AboutPage component.
  • The /main path renders the MainLayout component. It also takes up nested Routes as children and an error element in case something goes wrong.
  • Then we insert the router into the router prop of RouterProvider which handles the routing within.

However if you find this difficult as you have been using Route to declare the path. Don’t worry createRoutesFromElements comes to your rescue.

createRoutesFromElements

createRoutesFromElements is a function that allows you to define routes using JSX elements.It utilizes the familiar JSX syntax to create route configurations.

  • Define routes using JSX, making the structure clear and familiar.
  • Easily define nested routes within JSX, improving readability.
  • Use the generated route objects directly with createBrowserRouter.
import React from 'react';
import { createBrowserRouter, RouterProvider, Route } from 'react-router-dom';
import { createRoutesFromElements } from 'react-router';

import HomePage from './HomePage';
import AboutPage from './AboutPage';
import MainLayout from './MainLayout';
import NotFound from './NotFound';
import Dashboard from './Dashboard';
import Profile from './Profile';

const router = createBrowserRouter(
  createRoutesFromElements(
    <>
      <Route path="/" element={<HomePage />} />
      <Route path="/about" element={<AboutPage />} />
      <Route path="/main" element={<MainLayout />} errorElement={<NotFound />}>
        <Route path="dashboard" element={<Dashboard />} />
        <Route path="dashboard/profile" element={<Profile />} />
      </Route>
    </>
  )
);

const App = () => <RouterProvider router={router} />;

export default App;

Enter fullscreen mode Exit fullscreen mode

This code works the exact same as the previous example but this will be more familiar in comparison to the previous one.

Loader Functions

Loader functions in React Router enable you to fetch data before rendering a route. This ensures that your components have the necessary data available when they are displayed, improving the user experience.

export async function homePageLoader() {
  const response = await fetch('/api/homedata');
  if (!response.ok) {
    throw new Error('Failed to fetch home data');
  }
  const data = await response.json();
  return data;
}

Enter fullscreen mode Exit fullscreen mode

This loader function fetches data from an API endpoint ('/api/homedata'). If the fetch operation fails, it throws an error. Otherwise, it returns the fetched data.

useLoaderData

The useLoaderData hook in React Router is a powerful tool that allows you to fetch data for your routes and make it available to your components before they are rendered. This can be useful for a number of reasons, such as improving performance, preventing empty states, and providing a better user experience.

Why is useLoaderData useful?

  • Reduces the amount of network requests made in your application and increases performance by retrieving data for routes before they are rendered.
  • Keeps users from seeing empty states by providing components with data before they are rendered.
  • The faster you can provide data to your users, the better your application will work for them.
import { useLoaderData } from 'react-router-dom';

const HomePageComponent = () => {
  const data = useLoaderData();

  if (data.isLoading) {
    return <div>Loading...</div>;
  }

  if (data.isError) {
    return <div>Error loading data.</div>;
  }

  // Render the data
  return (
    <ul>
      {data.map((currentData) => (
        <li key={currentData.id}>{currentData.title}</li>
      ))}
    </ul>
  );
};

export default HomePageComponent;

Enter fullscreen mode Exit fullscreen mode

Then to make use of the loader use the loader prop in the route to initialize it.

import HomePage, {homePageLoader} from './HomePage';

//Rest of the code

<Route path="/" element={<HomePage />} loader={homePageLoader} />

Enter fullscreen mode Exit fullscreen mode

In this example, the HomePageComponent uses the useLoaderData hook to access the data loaded by the homePageLoader function. This allows the component to render with the fetched data.

useRouteError

Handling errors in your routes is crucial for providing a smooth user experience. The useRouteError hook allows you to catch and display errors that occur during route loading. This helps in handling failures and informing users about issues.

import { useRouteError } from 'react-router-dom';

function ErrorBoundary() {
  const error = useRouteError();

  return (
    <div>
      <h1>Error</h1>
      <p>{error.message}</p>
    </div>
  );
}

Enter fullscreen mode Exit fullscreen mode

In this example, the ErrorBoundary component uses the useRouteError hook to access the error object. If an error occurs during the loading of a route, this component displays an error message.

Then to make use of the ErrorBoundary the errorElement prop in the route to initialize it.

mport HomePage, { homePageLoader } from './HomePage';

import ErrorBoundary from './ErrorBoundary';

//Rest of the code 

 <Route path="/" element={<HomePage />} loader={homePageLoader} errorElement={<ErrorBoundary />}>

Enter fullscreen mode Exit fullscreen mode

Conclusion

React Router's createBrowserRouter, createRoutesFromElements, loader functions, useLoaderData, and useRouteError provide a robust and flexible toolkit for building dynamic React applications. By using these features, you can ensure your components have the necessary data before rendering and handle errors gracefully, resulting in a better user experience. Happy coding!

💖 💪 🙅 🚩
vikram-boominathan
Vikraman

Posted on June 7, 2024

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related