Data fetching with React Router
Tags: react, beginners, tutorial, api
Published: 2023-05-02 01:40:09

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](https://reactrouter.com/en/main/start/tutorial)._

### 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
```

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'
```

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* />
        },
        ...
    ]
  }
]}
```
Finally, add the following to the bottom of your `main.jsx` file:

```
ReactDOM.createRoot(document.getElementById('root')).render(
  <RouterProvider router={router} />
)
```
_See these [React Router docs](https://reactrouter.com/en/main/routers/create-browser-router) 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`);
},
```

Your component should now look like this:

```
const router = createBrowserRouter([
  {
    element: <Root />,
    id: "root",
    loader: async () => {
      return fetch(`your_fetch_url`);
    },
    children: [
    ...
    ]
  }
]}
```
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`);
  },
},
...
```
### 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";
```

Next, create a variable using the imported hook:

```
export default function YourComponent () {
  const your_data = useRouteLoaderData("root")
  ....
}
```
Notice how the useRouteLoader data hook takes a route `id` as a parameter. You can find more information about this hook [here](https://reactrouter.com/en/main/hooks/use-route-loader-data).

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()
```
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.