Back

Mutations and Caching with Redux-Toolkit-Query

Mutations and Caching with Redux-Toolkit-Query

This comprehensive tutorial will break down some advanced concepts (mutation, fetching, and caching) associated with the Redux Toolkit Query package. It will also show you how to use RTK Query and its hooks in the Redux Toolkit package to retrieve data from a REST API in a React application.

If you’ve been using React for a while, you’ve probably come across the terms Redux and Redux Toolkit. But what is the purpose of the Redux and Redux Toolkit?

In this tutorial, we won’t just explain these terms; we’ll also show how to handle data fetching with query endpoints in a sophisticated way using the fetchBaseQuery and createApi modules.

Conditional mutation makes it possible to invalidate particular cached data according to the tags. By doing this, it is decided which cached data will be requested again or deleted.

Thanks to RTK Query, a Redux Toolkit feature, implementing the essential ideas of mutation, conditional mutation, and caching is rather easy.

Using the createApi module, which includes the functions createApi(), fetchBaseQuery(), and Provider RTK queries, can be used in React-Redux applications.

We will cover concepts like:

  • The creation of a query endpoint.
  • Registration for the main Redux store.

Then we’ll touch on more advanced features like creating a conditional mutation and caching, in addition to other things.

To follow along with this article, you’ll need the following:

  • Node.js version 10.16.0 or higher installed on your computer.
  • A new React project set up with Create React App by following the documentation link.

Let’s dive right in!

What Is Redux-Toolkit-Query?

RTK Query is a powerful data fetching and caching tool. It is designed to simplify common cases for loading data in a web application, removing the need for you to manually write data fetching and caching logic.

In the Redux Toolkit package, there is an optional add-on called RTK Query. Its features are built on top of other APIs.

To display data, web applications need to fetch data from a server. They typically also need to update that data, send those updates to the server, and maintain synchronization between the cached data on the client and the server.

Why Use Redux-Toolkit-Query In React?

React Query and Redux-Toolkit-Query can be primarily classified as “Javascript Utilities & Libraries” tools.

Unlike React Query, RTK Query draws inspiration from other tools, such as React Query, Urql, and SWR, that have pioneered approaches to data retrieval. Still, it also adds a distinctive approach to its architecture.

  • Redux Toolkit’s createSlice and createAsyncThunk APIs are the foundation for the logic to fetch data and cache it.
  • RTK Query can also create React hooks that manage the lifetime of cached data as components mount and unmount, give components data and isLoading fields, and encapsulate the entire data fetching process.

Redux-Toolkit-Query(RTK-Query) Setup

It is incredibly easy to use RTK-Query with React. You require the following three things:

  • An existing React project
  • To install Redux-toolkit with npm/yarn
  • An API endpoint for making requests

The quickest way to start new apps with React and Redux is by using official redux guide for create react apps:

npx create-react-app my-app --template redux

If you already have a React project, all you have to do is install RTK using npm (or another package manager):

npm install @reduxjs/toolkit

RTK Query is included within the installation of the core Redux Toolkit package. Both of the following two entry points provide access to it:

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

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

How To Retrieve Data With Query Endpoint

Query endpoints are defined by returning an object from the endpoints section of createApi and defining the fields with the builder.query() function.

Create the ‘Component’ folder in the ‘src’ directory, create the Component/API folder, and create the api file named src\components\API\CryptApi.js, then add the given code to the file.

import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'

export const CryptApi = createApi({
  reducerPath: 'apiSlice',
  baseQuery: fetchBaseQuery({
    baseUrl: 'https://jsonplaceholder.typicode.com',
  }),
  endpoints: (builder) => ({
    getPosts: builder.query({
      query: () => '/posts',
    }),
  }),
})
export const { useGetCryptosQuery
 } = CryptApi

createAPI(): The functionality of createAPI() is the foundation of RTK Query. It enables you to specify a set of endpoints that describe how to fetch and transform data from many endpoints, including configuration. fetchBaseQuery(): A compact wrapper for fetch called fetchBaseQuery() aims to make requests easier. baseQuery(): is intended to be the method that most users should use in createApi().

In any React project, the CryptApi() should be used with one api slice per base URL; this is the suggested method for creating api slices.

Session Replay for Developers

Uncover frustrations, understand bugs and fix slowdowns like never before with OpenReplay — an open-source session replay tool for developers. Self-host it in minutes, and have complete control over your customer data. Check our GitHub repo and join the thousands of developers in our community.

Mutations And How To Define Mutation Endpoint

From the endpoint API above, it’s clear that we are trying to fetch data from baseQuery().

Using mutation, we want to modify and update the given data. The local cache is updated by using mutations, which are used to send server-side data updates. Another way that mutations can force re-fetches is by invalidating cached data.

Most of the time, you can use cache tag invalidation to perform a conditional mutation, which will force a query to re-fetch its data when it has been informed that a mutation has occurred that would cause its data to become out of date.

This will allow you to receive up-to-date data after triggering a change in the backend.

import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'

export const CryptApi = createApi({
  reducerPath: 'apiSlice',
  baseQuery: fetchBaseQuery({
    baseUrl: 'https://jsonplaceholder.typicode.com',
  }),
  endpoints: (builder) => ({
    getPosts: builder.query({
      query: () => '/posts',
    }),
 UpdatePosts: builder.mutation({
      query: ({id, ...rest}) => ({
        url:'/posts',
        method: 'PUT',
        body: rest
      })
    }),
  }),
})
export const 
{ 
useGetPostsQuery,
useUpdatePostsQuery
} = CryptApi

Observe the UpdatePosts() method, passed by mutation. This enables the endpoint to manipulate our database and change or modify data models.

Conditional Mutations Of Query Endpoint(automating re-fetch)

To automate re-fetching for query endpoints with data modified conditionally by mutation endpoints, RTK Query employs a cache tag scheme.

This allows you to design your API so that when a given mutation is fired, a specific query endpoint considers its cached data invalid and re-fetches the data if there is an active subscription.

Each endpoint/parameter combination provides its own queryCacheKey. The cache tag system allows RTK Query to be notified when a particular query cache provides specific tags. Suppose a mutation is fired that is said to invalidate tags provided by a query cache. In that case, the cached data is considered invalidated and must be re-fetched conditionally if there is an active subscription to the cached data.

Tags are simply names you can provide to specific data collections in RTK Query to regulate caching and invalidation behavior for conditional mutation purposes. It can be thought of as a label applied to cached data that is read after a mutation to determine whether or not the mutation should change the data.

When establishing an API, tags are defined in the tagTypes argument. TagTypes could be defined in an application that includes both Posts and Users, for example: ['Post', 'User'] when calling createApi.

import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'

export const CryptApi = createApi({
  reducerPath: 'apiSlice',
  baseQuery: fetchBaseQuery({
    baseUrl: 'https://jsonplaceholder.typicode.com',
  }),
  tagTypes: ['Post', 'PUT'],
  endpoints: (builder) => ({
    getPosts: builder.query({
      query: () => '/posts',
    }),
 UpdatePosts: builder.mutation({
      query: ({id, ...rest}) => ({
        url:'/posts',
        method: 'PUT',
        body: rest
      })
    }),
  }),
})
export const 
{ 
useGetPostsQuery,
useUpdatePostsQuery
} = CryptApi

Note the tagTypes: ['Post', 'PUT'] passed into creatApi.

Caching Query Endpoint Data

In some circumstances, you may manually update the cache. You can use the updateQueryData thunk action on the util object of your built API to update cache data already existing for query endpoints.

You can use an optimistic update to conduct an update to cache data immediately after a mutation is triggered. This can be a beneficial pattern when you want to give the user the appearance that their changes are immediate, even if the mutation request is still in flight.

The following are the key concepts for an optimistic update: When you initiate a query or mutation, the function onQueryStarted is called.

You manually refresh the cached data by calling api.util.updateQueryData from the onQueryStarted event.

If queryFulfilled rejects:

You roll it back using the .undo attribute of the object returned by the previous dispatch.

import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'

export const CryptApi = createApi({
  reducerPath: 'apiSlice',
  baseQuery: fetchBaseQuery({
    baseUrl: 'https://jsonplaceholder.typicode.com',
  }),
  endpoints: (builder) => ({
    getPosts: builder.query({
      query: () => '/posts',
    }),
 UpdatePosts: builder.mutation({
      query: ({id, ...rest}) => ({
        url:'/posts',
        method: 'PUT',
        body: rest
      })
    }),
    async onQueryStarted({ id, ...rest }, { dispatch, queryFulfilled }) {
        const patchResult = dispatch(
          api.util.updateQueryData('getPost', id, (draft) => {
            Object.assign(draft, rest)
          })
        )
        try {
          await queryFulfilled
        } catch {
          patchResult.undo()
        }
      },
    }), 
  }),
export const 
{ useGetPostsQuery,useUpdatePostsQuery
} = CryptApi

Suppose you have access to the store instance’s dispatch method. In that case, you can dispatch the result of invoking updateQueryData to update the cache data for a query endpoint if the corresponding cache entry exists.

Connect API Endpoint To Redux Store

Now, I will show you how to register a query endpoint using the configureStore() module from the redux toolkit. Create the src/app/ folder, then create the store.js file and add the code to the file.

import { configureStore } from '@reduxjs/toolkit';
import { cryptApi } from '../components/API/CryptApi';

export default configureStore({
  reducer: {
    [cryptApi.reducerPath]: cryptApi.reducer,
  },
});

Configure Query Endpoint And Provider To Store

Open the index.js file that is located in the src/ folder around the App component, define the Provider, import the store module already created, and pass the store to the api property as so:

import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import { Provider } from 'react-redux';
import store from './Store/store';

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <React.StrictMode>
    <Provider store={store}>
        <App />
      </Provider>
  </React.StrictMode>
);

Display Data With RTK-Query

Next, you have to create the src\components\Home.jsx file. In this file, we are going to retrieve the store data and display it via the component

import React from 'react';
import { useGetCryptosQuery } from '../components/API/CryptApi';

const { Title } = Typography;
const Home = () => {
  const {
  data, 
  isFetching, 
  } = useGetCryptosQuery();
  const apiData = data?.data?.stats;

  if (isFetching) return <p>Loading...</p>;
  return (
  <>
  console.log(apiData)
  </>
 );
};

export default Home;

In this section, we will build the frontend part of our app using the useGetPostsQuery hook that we built using CryptApi().

To do this, import the useGetPostsQuery hook first, as it offers the isFetching and data properties.

These attributes support us in managing data fetching. Specify the HTML structure for displaying the post data, create the Home function, and pass the content as props.

Conclusion

I think Redux Toolkit Query delivers a better developer experience, especially compared to how difficult Redux used to be before RTK’s release.

In this brief tutorial, we learned how to create a query endpoint in the React JS environment and use the fetchBaseQuery, createApi, and RTK query hooks to make data fetching incredibly simple.

Using the tagTypes method, we put conditional mutation into practice. We also learned how to update data to a database using endpoint mutation and handle data cache using updateQueryData.

Here is the GitRepo that you can download to thoroughly understand the code.

A TIP FROM THE EDITOR: For more on Redux Toolkit, don’t miss our Fetching Data In Redux Using RTK Query and Leverage data from RapidApi using Redux Toolkit articles.

Gain Debugging Superpowers

Unleash the power of session replay to reproduce bugs and track user frustrations. Get complete visibility into your frontend with OpenReplay, the most advanced open-source session replay tool for developers.

OpenReplay