Data fetching using RTK query

Mar 28, 2023by, Nimy Mathew

Technology

Managing state in a React application can be challenging, especially when it comes to data fetching. Traditional data fetching libraries like axios or fetch can quickly become complex and error-prone, leading to spaghetti code and performance issues. Fortunately, Redux Toolkit offers a powerful and intuitive alternative: RTK Query.

RTK Query is an optional add-on included in the Redux Toolkit package that eliminates the need to hand-write data fetching & caching logic yourself. RTK Query simplifies the process of fetching and caching data by automatically generating reusable API hooks based on a description of your API endpoints. In addition, RTK Query also provides several other benefits, including 

  • Automatic cache management thus reducing the number of network requests your application needs to make, 
  • Real-time updates through WebSocket connections or polling, allowing your application to respond to changes in the backend data without needing to refresh the page
  • TypeScript compatibility, providing type safety for your API calls and reducing the risk of runtime errors.

RTK Query is based on the feature concepts and API design patterns made popular by libraries like React Query, SWR, Apollo, and Urql.

In this article, we’ll explore some of the key features of RTK Query, and show you how to get started with it in your projects.

To switch to RTK Query, you need to install the necessary dependencies: `@reduxjs/toolkit`, `@reduxjs/toolkit/query` and `react-redux`.

The APIs included in the toolkit package are:

  • createAPI
  • fetchBaseQuery()
  • <ApiProvider />
  • setupListeners()

We will get to know in detail each one of them: 

`createAPI` allows us to create an API slice that defines endpoints for communicating with the server. 

The main options available for using createAPI are:

  • reducerPath: A string that defines the name of the slice in the Redux store where the API data will be stored. This is a required option.
  • baseQuery: An object that defines the base fetch function used for making HTTP requests. This is typically created using the fetchBaseQuery function from @reduxjs/toolkit/query.
  • endpoints: It is a  function that should take a builder object as its argument, which has methods for defining the API endpoints and their associated options.
    There are two types of endpoints:
    query – for retrieving data
    mutation – for modifying data.
    Both query and mutation methods have options to configure their behavior, such as the HTTP method to use, the URL to call, and whether or not to include a request body.
  • prefetch: An optional boolean or function that controls whether or not the API data is preloaded when the app first loads. 
  • tagTypes: An optional array of strings that define the types of cache tags that your API endpoints use. This can be used to configure cache eviction policies and to invalidate cache entries.
  • keepUnusedDataFor: An optional number of milliseconds that specifies how long unused data will be kept in the cache before being evicted.

There are additional options and configuration options available in the documentation that you can explore to further customize and optimize your API.

`fetchBaseQuery` provides options that allow you to customize the behavior of the base fetch function that RTK Query generates, giving you fine-grained control over how your API requests are made and processed. You can customize this object to add default headers, query parameters, and other options for your API requests.

Here’s an example of how to use createApi and fetchBaseQuery to create an API function:

import { createApi } from '@reduxjs/toolkit/query';

export const myApi = createApi({
 reducerPath: 'myApi',
 baseQuery: fetchBaseQuery({ baseUrl: 'https://api.example.com' }),
 endpoints: (builder) => ({
   getUsers: builder.query({
     query: () => '/users',
     transformResponse: (response) => response.data,
   }),
   updateUser: builder.mutation({
     query: (userID, userData) => ({
       url: `/posts/${userID}`,
       method: 'PUT',
       body: userData,
     }),
   }),
 }),
});

`ApiProvider` is a component provided by RTK Query that allows you to provide your API instance to your application.The ApiProvider component takes an api prop that should be set to the instance of your API generated by createApi. It also takes any children components that should have access to the API hooks provided by RTK Query. 

`setUpListeners` is a utility that helps you set up listeners for RTK Query lifecycle events in your Redux store. It is also used to enable refetchOnMount and refetchOnReconnect behaviors. By default, the listeners will log information about the events to the console, but you can customize the behavior by passing in an object with functions for each event you want to listen to. Below, you can see how we have used the created ‘myApi’ function in the app while configuring the store.

import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import { configureStore } from '@reduxjs/toolkit';
import { ApiProvider } from '@reduxjs/toolkit/query/react';
import { myApi } from './api';

const store = configureStore({
 reducer: {
   // Add your reducers here
   [myApi.reducerPath]: myApi.reducer,
 },
 middleware: (getDefaultMiddleware) =>
   getDefaultMiddleware().concat(myApi.middleware),
});

ReactDOM.render(
 <React.StrictMode>
   <Provider store={store}>
     <ApiProvider api={myApi}>
       {/* Add your app components here */}
     </ApiProvider>
   </Provider>
 </React.StrictMode>,
 document.getElementById('root')
);

setupListeners(store.dispatch, {
 onQueryStarted: (query) => {
   console.log(`Query ${query.requestId} started`);
 },
 onQueryFulfilled: (query) => {
   console.log(`Query ${query.requestId} fulfilled with data`, query.data);
 },
 onQueryRejected: (query) => {
   console.error(`Query ${query.requestId} rejected with error`, query.error);
 },
});

By wrapping your app components with the ApiProvider component, you can now use the generated API hooks to fetch and manage data from your API endpoints. For example, if you have an endpoint named getUsers, you can use the useGetUsersQuery hook to fetch the user data. 

import { useGetUsersQuery } from './api';

function UserList() {
 const { data, loading, error } = useGetUsersQuery();
 const [updateUser] = useUpdateUserMutation();

 const handleUpdatePost = async (userID, userData) => {
   try {
     const result = await updatePost(userID, userData).unwrap();
     console.log(result); // log the successful result
   } catch (error) {
     console.error(error); // log the error
   }
 };

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

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

 return (
   <ul>
     {data.map((user) => (
       <li key={user.id}>{user.name}</li>
     ))}
   </ul>
 );
}

With RTK Query, you can define your API endpoints using a simple and declarative syntax, and use the generated hooks to fetch and update data in your React components. You can also customize your endpoints and queries with a wide range of options and parameters, and benefit from built-in features such as caching, polling, and automatic error handling.

Overall, RTK Query can help you streamline your code and reduce boilerplate, while improving the performance and scalability of your application. If you’re using Redux Toolkit in your project, RTK Query is definitely worth checking out as a powerful and convenient alternative to traditional data fetching libraries.

Ideas for innovative projects buzzing in your mind? We can be the best development partner. Connect with us here to start something great!

Disclaimer: The opinions expressed in this article are those of the author(s) and do not necessarily reflect the positions of Dexlock.

  • Share Facebook
  • Share Twitter
  • Share Linkedin