DEV Community

Michael Marcoux
Michael Marcoux

Posted on

Data fetching with React Router

This blog will cover several ways that you can fetch data without a useEffect hook using the React Router library. Following these steps will allow you to write cleaner, more intuitive code as all of your data fetching will occur in a single React Router component called a BrowserRouter.

This tutorial assumes you have a functioning React app with React Router v6.4 installed. My recommendation for beginners is to use the Vite development environment and following the React Router tutorial here.

Step 1: Create your BrowserRouter

The React Router tutorial linked above will cover the BrowserRouter component in greater depth, though I wanted to fill in the gaps that I encountered while setting up my first Vite + React Router app. If you already have your Browser Router set up, skip this step.

After getting started with the Vite/React template, your project root directory should contain a src directory with components and routes directories listed inside of it. Inside of the src directory, you should have a main.jsx file. This file represents your parent JSX file and is the only JSX file referenced in your HTML document.

your_project/src/main.jsx
Enter fullscreen mode Exit fullscreen mode

At the top of main.jsx import the necessary components from React Router.

import React from 'react'
import ReactDOM from 'react-dom/client'
import { createBrowserRouter, RouterProvider } from 'react-router-dom'
Enter fullscreen mode Exit fullscreen mode

After all of your imports, add the BrowserRouter component using the following structure:

const router = createBrowserRouter([
  {
    element: <Root />,
    id: "root",
    children: [
        {
          path: '/',
          element: <Home />
        },
        {
          path: '/*your_route*',
          element: <*Your_Route_Component* />
        },
        ...
    ]
  }
]}
Enter fullscreen mode Exit fullscreen mode

Finally, add the following to the bottom of your main.jsx file:

ReactDOM.createRoot(document.getElementById('root')).render(
  <RouterProvider router={router} />
)
Enter fullscreen mode Exit fullscreen mode

See these React Router docs for more support on this step.

Step 2: Using the 'loader' API

At this point, it is best to think about which routes you will need to send data to. In this example, I will go over using the loader API to fetch data for the Root component for global use across my application (similar to passing state via React context) as well as fetching data on an individual route level.

Adding a loader to the Root component:
As mentioned above, this technique is especially useful if there is data that you will need in multiple components across your web application.

Going back to your router component in the main.jsx file, add the following under the id key:

loader: async () => {
  return fetch(`your_fetch_url`);
},
Enter fullscreen mode Exit fullscreen mode

Your component should now look like this:

const router = createBrowserRouter([
  {
    element: <Root />,
    id: "root",
    loader: async () => {
      return fetch(`your_fetch_url`);
    },
    children: [
    ...
    ]
  }
]}
Enter fullscreen mode Exit fullscreen mode

Sweet! Now your data is available in any child component when you import it with reference to its id: root.

Adding a loader to a child component
If you want to fetch data for a specific child component, the syntax is the same, but you will not need to include a value for id.

...
{
  path: '/*your_route*',
  element: <*Your_Route_Component* />
  loader: async () => {
      return fetch(`your_fetch_url`);
  },
},
...
Enter fullscreen mode Exit fullscreen mode

Step 3: Importing the fetched data

Now that you've set up your BrowserRouter to fetch data, you can navigate to your various components and import it. In order to import the data you fetched at your 'root' level, import the following at the top of your component:

import React from 'react'
import ReactDOM from 'react-dom/client'
import { useRouteLoaderData } from "react-router-dom";
Enter fullscreen mode Exit fullscreen mode

Next, create a variable using the imported hook:

export default function YourComponent () {
  const your_data = useRouteLoaderData("root")
  ....
}
Enter fullscreen mode Exit fullscreen mode

Notice how the useRouteLoader data hook takes a route id as a parameter. You can find more information about this hook here.

Importing data that you fetched directly to a component is similar, except you will use the useLoaderData hook instead and not specify a route id.

import { useLoaderData } from "react-router-dom"

export default function YourComponent () {
 const your_data = useLoaderData()
Enter fullscreen mode Exit fullscreen mode

Amazing! You've now fetched your data for various components without a single useEffect. Your code is cleaner, and you can easily see which data is going to each of your routes in the BrowserRouter component.

Top comments (0)