React Query: The game changer for making requests in your react app

React Query: The game changer for making requests in your react app

React Query is a data fetching and state management library for React applications. It simplifies the process of fetching data, it provides hooks for managing, caching, and synching asynchronous and remote data in React.

Benefits of React Query

  • React query removes the necessity of writing redundant code and provides built-in support for managing error-prone aspects of data retrieval, such as automatic caching, pagination, and background data fetching, for full application optimization.

  • It eliminates repetitive code for handling data fetching and helps the developer focus more on building the application while letting React Query handle the data layer effortlessly.

  • React query pre-fetches data in the background, after an update network request.

  • It revolutionizes how you make API calls by providing a powerful and intuitive library that simplifies the process.

  • It is widely used, highly customizable and lightweight.

Traditional Data Fetching Methods

These methods are used to fetch data, they include:

  • Fetch API

  • React useEffect Hooks

  • Axios

  • XHR

Fetch API

The Fetch API provides a JavaScript interface for accessing and manipulating parts of the protocol, such as requests and responses. It also provides a global fetch() method that provides an easy, logical way to fetch resources asynchronously across the network.

Fetch API vs. React Query

  • Abstraction and Simplification: Fetch API provides a low-level interface for making HTTP requests, requiring manual handling of request configuration, response parsing, error handling, and caching. React Query removes the complexities of data fetching by providing a higher-level API. It simplifies data fetching, caching, and error handling, and reduces boilerplate code, thereby improving the developer’s productivity.

  • Automatic Background Refetching: Fetch API manually implements the logic for re-fetching data at specific intervals or in response to certain events, while React Query automatically handles Background data fetching. It manages re-fetching efficiently and removes the need for manual implementation.

  • Integration with React Ecosystem: Fetch API can be used directly with React, but it requires additional code and handling to integrate the React component lifecycle and state management. On the other hand, React Query is specifically designed for React Apps. It offers seamless integration with the React component lifecycle and popular state management libraries such as Redux or MobX. React Query provides a range of hooks and utilities that align perfectly with React's declarative programming tool.

React useEffect Hooks

These hooks lets you perform side effects in your components. They update data, fetch data and update timers.

React useEffect Hooks vs. React Query

  • Speed: Making API requests and fetching data is super slow. React Query is a smooth and faster option, unless you have a lot of time to spare for data fetching.

  • Glitch-prone: Fetching data with useEffect Hooks is glitch-prone and has too many errors due to complexity in logic, no one wants an application with too many errors and crashes, so React Query is the best option to avoid these issues.

  • Automatic Background Refetching: React useEffect Hooks does not update the new state and data fetching automatically. It is inefficient for complex logic.

Getting Started with React Query

To install React Query in a React App

  • Open a terminal and move to the root directory of your React app.

  • Install React Query using npm or yarn.

For Npm


npm install react-query

For Yarn


yarn add react-query
  • After installation, open the file you want to use React Query on

  • Import React Query on the first line of your file.

    
      import { QueryClient, QueryClientProvider } from 'react-query';
    
  • Create an instance of ‘QueryClient’

    
      const queryClient = newQueryClient();
    
  • Wrap your app component with the ‘QueryClientProvider’ component,and pass the ‘queryClient’ instance as a prop

    
      function App() { return ( {/\* Your app components \*/}
    
      </ QueryClientProvider>
    
      ); }
    

    You can configure the client options, using the ‘queryClient’ instance, some examples are shown below

Changing the Global Handling Behavior:


const queryClient = new QueryClient({ defaultOptions: { queries: { onError: (error) => { // Handle the error globally console.error('An error occurred:', error);

},

},

},

});

Invalidating Automatic Background Refetching for Queries:


const queryClient = new QueryClient({ defaultOptions: { queries: { refetchOnWindowFocus: false, },

},

});

Fetching Data with React Query

  • Declare a function that will fetch your data, this function should return a promise that resolves with the fetched data.

async function fetchData() { const response = await fetch('[https://api.example.com/data](https://api.example.com/data)'); const data = await response.json(); return data

;
  • Use the ‘useQuery’ hook to fetch the data and handle the loading,success and error states. Pass the ‘fetchData’ function as the first argument to the hook. In this example, ‘myData’ is the query key, used to identify data in the cache. React Query will handle all background updates.

  • Render your component within a ‘QueryClientProvider’ component in your app file.

  • Finally, render your app component in the root of your HTML file.

ReactDOM.render(<App />, document.getElementById('root'));

Handling loading and error states

function MyComponent() {
  const { isLoading, isError, data, error } = useQuery('myData', fetchData);

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

  if (isError) {
    return <div>Error: {error.message}</div>;
  }

  return <div>Data: {data}</div>;
}

In this example;

  • ‘isLoading’ property shows if the data is currently being fetched. If ‘isLoading’ is true, you can display a loading indicator.

  • ‘isError’ property indicates error while data is fetching. If ‘isError’ is true, you can display an error message.

  • Data fetching is successful if‘isLoading’ and ‘isError’ are false. You can access and render the ‘data’ property.

Configuring Query Options

React Query can be configured by using polling method;

Polling: By Default, React Query doesn’t fetch data automatically, it requires a trigger. While developing a real-time system, data needs to be fetched regularly from the backend to keep the frontend updated and in sync. Polling is a good option at this point, it repeatedly re-fetches data automatically from a remote data source at regular intervals, but how does this work? Implement the useQuery hook’s refetch Interval property. The specified time interval set will automatically fetch data, updating the front end and keeping it in sync.

const { data } = useQuery({
 queryKey: ['product'],
 queryFn: () => fetch( url 
).then( res =>res.json() ),
   refetchInterval: 3000,
})

Data Mutation with React Query

React Query useMutation hook is an important tool for handling asynchronous data in React applications. It makes the process of performing mutations and automatically updating your UI easy, so you do not have to worry about managing the background updates, caching, and pagination.

Sending requests with useMutation hook

  • Import the essential modules in your component
import { useMutation } from 'react-query';
  • Define your mutation function
const createUser = async (userData) => {
//Perform the actual HTTP user to create a user
const response = await fetch('/api/users'), {
 method: 'POST',
 headers: { 
   'Content-Type': 'application/json',
},
 body: JSON.stringify(userData),
});
 if (!response.ok) {
   throw new Error('An error occurred while creating the user');
  }
 return response.json();
};
  • Use the ‘useMutation’ hook, to define the mutation in your component
const MyComponent = () => {
 const createUserMutation = useMutation(createUser);

 const handleCreateUser = async () => {
  try {
    const userData = { name: 'John Doe', email: 'johndoe@example.com' };
//Call the mutation function with the user data
await createUserMutation.mutateAsync(userData);

//Handle successful response
console.log('User created successfully');
} catch (error) {
//Handle error
console.error(error);
}
};

return (
  <div>
  {/*Render your UI */}
  <button onClick ={handleCreateUser}>Create User</button>
  </div>
 );
};
  • You can access the mutation state and result using the ‘isLoading’ , ‘isError’ , ‘isSuccess’ , and ‘data’ properties of the ‘createUserMutation’ object
const handleCreateUser = async () => {
  try {
    // Call the mutation function with the user data
    await createUserMutation.mutateAsync(userData);
  } catch (error) {
    // Handle error
    console.error(error);
  }
};

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

if (createUserMutation.isError) {
  return <div>Error creating user</div>;
}

if (createUserMutation.isSuccess) {
  return <div>User created successfully</div>;
}

return (
  <div>
    {/* Render your UI */}
    <button onClick={handleCreateUser}>Create User</button>
  </div>
);
  • You can now send and manage requests with ‘useMutation’

Advanced Features and Integration

Pagination

Pagination is a series of interconnected pages with similar content. In react, it is a function of apps implemented to show data on series of pages. This process similar to server side technology, it allowa clients make group requests for data. Pagination displays data through a series of pages, rather than viewing all at once.

Pagination in React JS is especially useful for huge datasets, such as maps or visualizations, where the number of elements presented may exceed the space provided on a single page. Pagination, in this example, aids users in navigating enormous datasets by allowing them to hop without having to navigate from one webpage to the next reload each time they want to view anything new. A basic example of pagination is shown below:

<Pagination count={10} />

<Pagination count={10} color="primary" />

<Pagination count={10} color="secondary" />

<Pagination count={10} disabled />

Paginating data using useInfiniteQuery

  • Import the necessary dependencies
import { useInfiniteQuery } from 'react-query';
  • Define a function that fetches a page of data. This function should accept a ‘pageParam’ argument that represents the next page token or offset:
const fetchData = async (pageParam = 0) => {
  // Perform an API request to fetch a page of data using the pageParam
  const response = await fetch(`/api/data?page=${pageParam}`);
  const data = await response.json();

  // Return the fetched data
  return data;
};
  • Use the ‘useInfiniteQuery’ hook in your component, passing the ‘fetchData’ function and any additional options
const YourComponent = () => {
  const {
    data,
    fetchNextPage,
    hasNextPage,
    isFetchingNextPage,
    isError,
    isLoading,
  } = useInfiniteQuery('yourQueryKey', fetchData, {
    getNextPageParam: (lastPage) => lastPage.nextPageToken, // Adjust this based on your API response
  });

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

  if (isError) {
    return <div>Error occurred while fetching data.</div>;
  }

  return (
    <div>
      {data.pages.map((page, pageIndex) => (
        <React.Fragment key={pageIndex}>
          {page.items.map((item) => (
            // Render your data item here
            <div key={item.id}>{item.name}</div>
          ))}
        </React.Fragment>
      ))}
      {hasNextPage && (
        <button onClick={fetchNextPage} disabled={isFetchingNextPage}>
          {isFetchingNextPage ? 'Loading more...' : 'Load More'}
        </button>
      )}
    </div>
  );
};

I* n this example, the ‘useInfiniteQuery’ hook does the pagination for you, It fetches the initialPage automatically for you. It provides functions like ‘fetchNextPage’ to fetch subsequent pages, ‘isFetchingNextPage’ to manage UI state while loading the next page.

  • You can customize the rendering of data to your requirements and logic.

Infinite scrolling with react query

Infinite scrolling is a method, where a user continuous scrolling loads chunks of data. An example is Facebook’s news feed where a user continues to scroll without any end. It provides a smooth and engaging browsing experience for you.

Implementing infinite scrolling with react query

  • Install React Query
npm install react-query
  • Define your API endpoint that provides paginated data

  • Create a QueryKey for your paginated data

const queryKey = 'paginatedData';
  • Create a function that fetches the paginated data using the API endpoint. This function will be passed to the ‘useInfiniteQuery’ hook
const fetchPaginatedData = async (key, page = 1) => {
  const response = await fetch(`/api/data?page=${page}`);
  const data = await response.json();
  return data;
};
  • Import the ‘useInfiniteQuery’ hook from React Query and use it to fetch the paginated data
import { useInfiniteQuery } from 'react-query';

const MyComponent = () => {
  const { data, fetchNextPage, hasNextPage, isFetchingNextPage } = useInfiniteQuery(
    queryKey,
    ({ pageParam }) => fetchPaginatedData(queryKey, pageParam),
    {
      getNextPageParam: (lastPage) => lastPage.nextPage,
    }
  );

  // Render your component
};
  • Render the paginated data in your component’s render function
return (
  <div>
    {data.pages.map((page) =>
      page.data.map((item) => <div key={item.id}>{item.name}</div>)
    )}

    {hasNextPage && (
      <button onClick={() => fetchNextPage()} disabled={isFetchingNextPage}>
        {isFetchingNextPage ? 'Loading more...' : 'Load More'}
      </button>
    )}
  </div>
);
  • The ‘hasNextPage’ variable indicates whether there’s more data available. If it’s ‘true’, we render a “Load More” button. This button, when clicked, calls the ‘fetchNextPage’ function to fetch the next page of data.

  • These steps will help you implement the infinite scrolling function using React Query. React Query automatically fetches the next page of data and updates the component, as the user scrolls or clicks ‘Load More’ button.

React Query DevTools

React Query DevTools helps a developer, debug and check the activities that take place in a React application. It’s important to be aware every action that takes place while building your app. It helps the developer check data saved in the state and reset the state to refetch data. You can install React Query DevTools with this simple step.

  • Type this in your terminal
$ npm i @tanstack/react-query-devtools
  • Import it and render it where you render the ‘ReactQueryProvider’
import { QueryClientProvider } from '@tanstack/react-query';
import { ReactQueryDevtools } from '@tanstack/react-query-devtools';
import React from "react";
import { queryClient } from './react-query/client';
import Router from './Router';

function App() {
  return (
    <React.StrictMode>
      <QueryClientProvider client={queryClient}>
        ...
        <ReactQueryDevtools />
      </QueryClientProvider>
    </React.StrictMode>
  );
}
export default App;

React Query Best Practices and Tips

You can build highly performative and reliable applications, by using these best practices.

  • Customize query configurations based on your specific needs.

  • Implement pagination and infinite scroll for long lists of data.

  • Use query invalidation to manually invalidate and refetch specific queries when related to data changes.

  • Prefetch data for user experience optimization.

  • Use mutations to handle optimistic updates, retries, and error handling smoothly.

  • Use query keys wisely.

  • Normalize your data structures.

  • Write tests to ensure the implementation of functions and to unveil any issues.

    React Query is a robust, widely used tool that provides seamless data handling, caching and pagination,optimization, and high performance, which improves user experience.