DEV Community

Abhishek Panwar
Abhishek Panwar

Posted on • Edited on

Complete redux toolkit (Part -3 )

Part 3: Introduction to RTK Query

In this part we will cover RTK query

1. What is RTK Query?

While Redux Toolkit provides powerful tools to manage state and asynchronous logic, it still requires significant boilerplate code to handle data fetching and caching. RTK Query, introduced in Redux Toolkit v1.6, aims to solve this problem by providing a set of powerful tools for efficient data fetching and caching with minimal setup.

Key features of RTK Query:

  • Data Fetching and Caching: Automatically handles caching, invalidation, and refetching.
  • Optimistic Updates and Realtime Synchronization: Easily manage optimistic updates and real-time data synchronization.
  • Declarative and Simple API: Intuitive API design with minimal boilerplate code.
  • Integrated with Redux Toolkit: Built on top of Redux Toolkit, allowing seamless integration.

2. Setting Up RTK Query

To get started with RTK Query, we need to define an API service that specifies how to fetch data and what endpoints are available. Let’s create an example using a simple posts API.

Step 1: Define an API Service

Create a new file named postsApi.js in the features/posts directory. This file will define the API endpoints for fetching and mutating posts.

// src/features/posts/postsApi.js
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react';

// Define an API service using RTK Query
export const postsApi = createApi({
  reducerPath: 'postsApi',
  baseQuery: fetchBaseQuery({ baseUrl: 'https://jsonplaceholder.typicode.com/' }),
  endpoints: (builder) => ({
    fetchPosts: builder.query({
      query: () => 'posts',
    }),
    addPost: builder.mutation({
      query: (newPost) => ({
        url: 'posts',
        method: 'POST',
        body: newPost,
      }),
    }),
  }),
});

// Export hooks for usage in functional components
export const { useFetchPostsQuery, useAddPostMutation } = postsApi;
Enter fullscreen mode Exit fullscreen mode

Explanation:

  • createApi: This function is used to define an API service. It generates an API slice, automatically managing the store, reducers, and actions for you.
  • baseQuery: A function that defines the base URL for your API. fetchBaseQuery is a lightweight wrapper around the standard fetch API.
  • endpoints: A function that defines the endpoints for the API. We define two endpoints here: fetchPosts for querying data and addPost for creating a new post.

Step 2: Integrate API Service into the Redux Store

Add the postsApi reducer to the store and configure middleware to enable caching and invalidation.

Update store.js to integrate postsApi:

// src/app/store.js
import { configureStore } from '@reduxjs/toolkit';
import { postsApi } from '../features/posts/postsApi';

const store = configureStore({
  reducer: {
    // Add the generated reducer as a specific top-level slice
    [postsApi.reducerPath]: postsApi.reducer,
  },
  // Adding the api middleware enables caching, invalidation, polling, and other features of RTK Query
  middleware: (getDefaultMiddleware) =>
    getDefaultMiddleware().concat(postsApi.middleware),
});

export default store;
Enter fullscreen mode Exit fullscreen mode

3. Using RTK Query in Components

RTK Query generates custom hooks based on the endpoints defined in the API service. These hooks are used to perform data fetching, mutations, and manage caching automatically.

Step 1: Fetching Data with useFetchPostsQuery

Create a PostsList.js component to fetch and display the list of posts.

// src/features/posts/PostsList.js
import React from 'react';
import { useFetchPostsQuery } from './postsApi';

const PostsList = () => {
  const { data: posts, error, isLoading } = useFetchPostsQuery();

  if (isLoading) return <p>Loading...</p>;
  if (error) return <p>An error occurred: {error.message}</p>;

  return (
    <section>
      <h2>Posts</h2>
      <ul>
        {posts.map((post) => (
          <li key={post.id}>{post.title}</li>
        ))}
      </ul>
    </section>
  );
};

export default PostsList;
Enter fullscreen mode Exit fullscreen mode

Explanation:

  • useFetchPostsQuery: A custom hook generated by RTK Query for the fetchPosts endpoint. It returns an object containing the fetched data (data), loading state (isLoading), and error state (error).
  • The component conditionally renders loading, error, or data states based on the hook output.

Step 2: Adding Data with useAddPostMutation

Create a AddPostForm.js component to add new posts using the addPost mutation.

// src/features/posts/AddPostForm.js
import React, { useState } from 'react';
import { useAddPostMutation } from './postsApi';

const AddPostForm = () => {
  const [addPost, { isLoading }] = useAddPostMutation();
  const [title, setTitle] = useState('');
  const [content, setContent] = useState('');

  const handleSubmit = async (e) => {
    e.preventDefault();
    if (title && content) {
      await addPost({ title, body: content }).unwrap();
      setTitle('');
      setContent('');
    }
  };

  return (
    <section>
      <h2>Add a New Post</h2>
      <form onSubmit={handleSubmit}>
        <input
          type="text"
          value={title}
          onChange={(e) => setTitle(e.target.value)}
          placeholder="Post Title"
        />
        <textarea
          value={content}
          onChange={(e) => setContent(e.target.value)}
          placeholder="Post Content"
        />
        <button type="submit" disabled={isLoading}>
          {isLoading ? 'Adding...' : 'Add Post'}
        </button>
      </form>
    </section>
  );
};

export default AddPostForm;
Enter fullscreen mode Exit fullscreen mode

Explanation:

  • useAddPostMutation: A custom hook generated by RTK Query for the addPost mutation. It provides a function (addPost) to trigger the mutation and a loading state (isLoading).
  • unwrap(): Allows us to unwrap the resolved or rejected payload from the mutation to handle side effects after the request.

4. Handling Cache, Errors, and Optimistic Updates

RTK Query automatically handles caching, error states, and invalidates the cache when mutations occur. You can further customize the behavior with tags and other configurations.

Step 1: Using providesTags and invalidatesTags

Modify the postsApi to use tags for cache invalidation:

// src/features/posts/postsApi.js
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react';

export const postsApi = createApi({
  reducerPath: 'postsApi',
  baseQuery: fetchBaseQuery({ baseUrl: 'https://jsonplaceholder.typicode.com/' }),
  tagTypes: ['Post'],
  endpoints: (builder) => ({
    fetchPosts: builder.query({
      query: () => 'posts',
      providesTags: (result) =>
        result ? result.map(({ id }) => ({ type: 'Post', id })) : ['Post'],
    }),
    addPost: builder.mutation({
      query: (newPost) => ({
        url: 'posts',
        method: 'POST',
        body: newPost,
      }),
      invalidatesTags: ['Post'],
    }),
  }),
});

export const { useFetchPostsQuery, useAddPostMutation } = postsApi;
Enter fullscreen mode Exit fullscreen mode

Explanation:

  • providesTags: This is used to tag the data fetched from the fetchPosts query. It helps in efficiently invalidating the cache when new data is added.
  • invalidatesTags: This is used in the addPost mutation to invalidate the cache and refetch the updated data.

5. Conclusion and Next Steps

In this part, we explored how to use RTK Query to handle data fetching and caching in Redux applications. We covered setting up an API service, defining endpoints, and using generated hooks for querying and mutating data. RTK Query simplifies data fetching and state management with minimal code, making it a powerful tool for modern Redux applications.

In the next part, we'll dive into Advanced Topics in RTK Query, such as customizing queries, using baseQuery, handling authentication, and optimizing performance.

Stay tuned for Part 4: Advanced Topics in RTK Query!

Top comments (0)