Thursday, June 19, 2025

useFetcher() Hook in React Router With Example

In this post we'll see how to use useFetcher() hook in React Router. Generally, when we have a loader or action function any call to it also triggers a navigation.

For example, suppose we have a Form component defined with method and action prop as given below.

<Form method="post" action="/favPost">

If you are at "/post" path when this action is triggered then you will be navigated to "/favPost" path.

If you don't want to trigger this navigation; meaning you want to call the action associated with "/favPost" path but need to stay at the current path "/post" that can be done using useFetcher() hook in React Router.

The useFetcher hook in React Router allows for execution of route loaders and actions without triggering a full navigation. Any scenario where data has to be loaded or mutated in background, paginated data where you want to stay on the same page with next page data can be handled using useFetcher.

useFetcher hook in React

useFetcher hook returns an object.

const fetcher = useFetcher();

Some of the properties provided by the fetcher object are as-

  1. fetcher.state- Available states are "idle", "loading", "submitting".
  2. fetcher.data- The data returned from the called action or loader
  3. fetcher.Form- Form used to submit data to a route action without navigating away from the current page.
  4. fetcher.submit- To programmatically submit data to a route action without navigating away from the current page.
  5. fetcher.load- Fetching data from a loader function associated with a route without navigating away from the current page.

useFetcher hook React example

Suppose you have a scenario to show list of posts and also have an input element at the top to take user input for favourite post. While saving that favourite post you don't want to navigate to the "/favPost" route but want to stay at the post list page.

useFetcher() Hook in React Router

Route Configuration

import { createBrowserRouter } from "react-router";
import Home from "./home";
import NavigationNavLink from "./NavigationNavLink";
import ErrorPage from "./ErrorPage";
import PostList, {loader as postLoader} from "./PostList";
import PostLayout from "./PostLayout";
import FavPost, {action as favPostAction} from "./FavPost";


export const route = createBrowserRouter([
  {path: "/", element: <NavigationNavLink />, errorElement: <ErrorPage />,
   children: [
    {index: true, element: <Home /> },
    {path: "post", element: <PostLayout />,
     children:[
        {index: true, element: <PostList />, loader: postLoader, hydrateFallbackElement: <h2>Loading...</h2>},
     ]
    },
    {path: "favpost", element: <FavPost />, action: favPostAction}
   ]
  },
])

Components

For Navigation Menu, Home and ErrorPage components you can get the code from this post- Data Loader in React Router

src\components\routes\PostLayout.js

This component acts as a parent route component for all the Post related functionality. Child component is rendered in the place of Outlet which acts as a placeholder.

import { Outlet } from "react-router";
const PostLayout = () => {
  return(
    <div className="mx-2">
      <Outlet />
    </div>
  )
}

export default PostLayout;

src\components\routes\PostList.js

This component lists all the fetched post data.

import { useLoaderData } from "react-router";
import FavPost from "./FavPost";

const PostList = () => {
  const postData = useLoaderData();

  return(
    <> 
      <FavPost />
      <h2 className="text-info-emphasis text-center">Posts</h2>
      <ul className="list-group">
        {postData.map((post) =>
        <li className="list-group-item" key={post.id}>
            {post.id} {post.title}
        </li>
        )}
      </ul>
    </>
  )
}

export default PostList


export async function loader(){
  const response = await fetch('https://jsonplaceholder.typicode.com/posts?_limit=10');
  // check for any error
  if(!response.ok){
    // use Response object
    throw new Response("Failed to load data", { status: response.status });
  }else{
    const responseData = await response.json();
    return responseData;
  }
}

As you can see <FavPost> component is called with in the <PostList> component.

src\components\routes\FavPost.js

import { useEffect } from "react";
import { useFetcher } from "react-router";

const FavPost = () => {
  const fetcher = useFetcher();
  const {data, state} = fetcher;
  useEffect(()=>{
    if(state === "idle" && data && data.message){
        window.alert(data.message);
    }
  }, [data, state])
  return(
    <fetcher.Form method="post" action="/favPost">
      <label className="form-label" htmlFor="fav">Your favorite post</label>
      <input type="text" id="fav" name="favpost"></input>
      <button className="btn btn-success" type="submit">Save</button> 
    </fetcher.Form> 
  );
}

export default FavPost;

export async function action({request}){
  console.log('Saving favorite post data')
  const data = await request.formData();
  const favpost = data.get('favpost');
  // send to backend server to save favorite post...
  console.log(favpost);
  return { message: 'Saved successfully!' };
}

Some of the points to note here-

  1. useFetcher is used here to avoid navigation away from the current path.
  2. Using object destructuring data and state from fetcher are extracted to two variables.
  3. With in useEffect() hook there is a check that the state is “idle” (action execution is done), there is data then show the data.message.
  4. It is actually fetcher.Form which causes the action function to be called without navigating away from the current page. By changing it to
    you can see that it navigates to the “/favPost” path then.

That's all for this topic useFetcher() Hook in React Router With Example. If you have any doubt or any suggestions to make please drop a comment. Thanks!


Related Topics

  1. CRUD Example With React Router Loader and Action
  2. Data Loader With Dynamic Routes in React
  3. Actions in React Router For Data Mutation
  4. useRouteLoaderData() Hook in React Router
  5. useSearchParams in React Router - Handling Query Parameters

You may also like-

  1. JVM Run-Time Data Areas - Java Memory Allocation
  2. Difference Between Abstract Class And Interface in Java
  3. Armstrong Number or Not Java Program
  4. Spring Boot + Data JPA + MySQL REST API CRUD Example

No comments:

Post a Comment