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
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 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.
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.
Top comments (0)