An Overview of Redux RTK Query
Tags: data, react
Published: 2022-10-24 15:43:05

Have you ever heard of the Redux Toolkit? State management was developed from Redux and is known as the Redux Toolkit. The boilerplate code for Redux can be minimized using the Redux Toolkit, offering an excellent choice for both beginners and experienced developers. The Redux Toolkit has a ton of features, one of which is the RTK Query (Redux ToolKit Query), which is what we'll be exploring in this article.

**Prerequisites**

- Knowledge of React and Redux Toolkit.
- Node.js installed on your machine.
- Knowledge of CRUD operations with Fetch or Axios.


In this article, we’ll learn about RTK Query and how to utilize it in React to fetch data. For this, we'll use information from a fictitious JSON server to develop a straightforward to-do application.

The [RTK Query](https://redux-toolkit.js.org/rtk-query/overview) is a data retrieval and caching tool that is quite effective. It lets you avoid having to create data fetching and caching logic manually. RTK Query intended to simplify common instances for data loading in a web application. For example, usually, web applications perform CRUD operations from a server and maintain synchronization between the cached data on the client and the server.

The Redux Toolkit package installation comes with RTK Query, which includes the following API:

- [fetchBaseQuery()](https://redux-toolkit.js.org/rtk-query/api/fetchBaseQuery)**:** This is a condensed fetch wrapper that seeks to make requests easier. It’s designed to be the `baseQuery` that most developers should use with `createApi`.
- [createApi()](https://redux-toolkit.js.org/rtk-query/api/createApi)**:** This is the foundation of RTK Query's features. It enables you to specify a collection of "endpoints" that specify how to get data from async sources, such as backend APIs and other async sources, along with the setting of how to get the data and convert it. `createApi()` makes an "API slice" structure for you that comprises Redux logic and, optionally, React hooks, which enable you to fetch and cache data easily.
- [<ApiProvider/>](https://redux-toolkit.js.org/rtk-query/api/ApiProvider): You can use this as a `Provider` if you don't already have a Redux store.
- [setupListeners()](https://redux-toolkit.js.org/rtk-query/api/setupListeners)**:** This is a tool for enabling the `refetchOnFocus` and `refetchOnReconnect` characteristics. It requires the delivery strategy from your store. You can provide a callback for more precise control by doing `setupListeners(store.dispatch)`, which will configure listeners with the suggested defaults.


## Scaffolding a New React Project

It’s relatively easy to get started with React. You can use the React project creation tool to scaffold a new project with a few sample files to begin.

Use the terminal to run the following command to start a new React project:

```
yarn create react-app react-RTK Query
```

Once the project is created, remove the unnecessary files from the src folder after the project has been created.

Run the following command from the terminal to install the form library:

```
yarn add @reduxjs/toolkit react-redux
```

### Configuring the Redux Store

RTK Query will cache the data it has downloaded from the server in the Redux store, but to allow this, the store must first be configured. The Redux store has to be updated using the Redux slice reducer, and the custom middleware is generated automatically when the API slice is created.

Let's make a file called store.js in the src directory, our Redux store, and head over to our index.js file to enclose our `<App/>` with a provider:

#### Step 1

```
import { configureStore, getDefaultMiddleware } from "@reduxjs/toolkit";

import { todoApi } from "./TodoApi";

export const store = configureStore({
  reducer: {
    [todoApi.reducerPath]: todoApi.reducer,
  },
  middleware: (getDefaultMiddleware) =>
    getDefaultMiddleware().concat(todoApi.middleware),
});
```

_Configuration of Redux store_

#### Step 2

```
import { Provider } from "react-redux";
import { store } from "./Services/Store";
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
  <React.StrictMode>
    <Provider store={store}>
      <App />
    </Provider>
  </React.StrictMode>
);
```

_Wrapping the_ `<App/>` _component with a provider_

## Implementing CRUD Operations

### Queries

The most typical application of RTK Query is with CRUD operations. However, you are generally advised to only use queries for requests that receive data. A query operation can be performed with any fetching data library. It’s best to use a Mutation for anything that modifies data on the server or could potentially invalidate the cache.

By default, RTK Query includes `fetchBaseQuery`, a lightweight fetch wrapper that automatically manages request headers and answers parsing in a manner akin to that of widely used libraries like Axios.

Let's begin with creating a new file called `TodoApi.js` and import the following:

```
import { createApi, fetchBaseQuery} from "@reduxjs/toolkit/query/react";

export const todoApi = createApi({
  reducerPath: "todoApi",
  baseQuery: fetchBaseQuery({
 baseUrl: "https://jsonplaceholder.typicode.com",
  }),
  endpoints: (builder) => ({})
});
```

The `createApi()` takes an object with the following properties:

[reducerPath](https://redux-toolkit.js.org/rtk-query/api/createApi#reducerpath): This particular key specifies the location of the cache's storage in the Redux store.

[baseQuery](https://redux-toolkit.js.org/rtk-query/api/createApi#basequery): This enables us to construct a query by only supplying the base URL — each endpoint's default query for data requests.

[Endpoints](https://redux-toolkit.js.org/rtk-query/api/createApi#endpoints) This is a collection of activities that you want to be carried out on your server. With the help of the builder syntax, you define them as an object.

_N/B: There are two basic endpoint types:_ [query](https://redux-toolkit.js.org/rtk-query/usage/queries) _and_ [mutation](https://redux-toolkit.js.org/rtk-query/usage/mutations)_._

**Query:** They serve as endpoints for requests to READ data, specifically for reading data from the server.

**Mutation:** These data updates are sent to the server through mutations, and the local cache is updated. Mutations may also invalidate cached information and necessitate re-fetches, e.g., CREATE, UPDATE, DELETE.

### Specifying endpoints

```
endpoints: (builder) => ({
    addTodo: builder.mutation({
      query: (todo) => ({
        url: "/posts",
        method: "POST",
        body: JSON.stringify({
          title: todo.title,
          body: todo.body,
          id: todo.id,
        }),
        headers: {
 "Content-type": "application/json; charset=UTF-8",
        },
      }),
      invalidatesTags: ["Todo"]
    }),
    getAllTodos: builder.query({
      query: () => "/posts",
      providesTags: ["Todo"]

    }),
    updateTodo: builder.mutation({
      query: ({ id, ...todo }) => ({
        url: `/posts/${id}`,
        method: "PUT",
        body: JSON.stringify({
          title: todo.title,
          body: todo.body,
        }),
        headers: {
 "Content-type": "application/json; charset=UTF-8",
        },
      }),
      invalidatesTags: ["Todo"]

    }),
    deleteTodo: builder.mutation({
      query: (id) => ({
        url: `/posts/${id}`,
        method: "DELETE",
      }),
      invalidatesTags: ["Todo"]

    }),
  })
```

These are the endpoints for performing the **CRUD** operations:

_**addTodo**_: Endpoint of a mutation for creating a todo.

_**getAllTodos**_: Query endpoint in charge of retrieving all the todos from the server.

_**updateTodo**_: Endpoint of a mutation for updating a todo.

_**deleteTodo**_: Endpoint of a mutation for deleting a todo.

Now that we have our endpoints, you may be wondering, “How can we utilize them?”

The RTK Query automatically generates a React hook for us to access the endpoints, and they usually begin with "use\*NameOfEndpoint\*\*Endpoint type\*:

```
//auto generated hook
export const {
  useAddTodoMutation,
  useDeleteTodoMutation,
  useGetAllTodosQuery,
  useUpdateTodoMutation,
} = todoApi;
```

Auto-Generated Hook

### Fetch All Todos

Let's head over to our App.js file, where we display our todos to the DOM. We also need to import the auto-generated hook:

```
import { useGetAllTodosQuery } from "./Services/TodoApi";
```

When a query hook is called, it returns an object with properties like the most recent information for the query request and status booleans for the request's lifecycle state. The most popular properties are shown below:

```
const { data, error, isLoading, isFetching, refetch } = useGetAllTodosQuery();
```

_Destructuring data from the auto-generated hook_

**data**: This is the most recent result returned, if any.

**error**: This results in an error, if any.

**isLoading**: When true, it means that the Query is presently loading for the first time and that no data has been returned. The first request sent out will be fulfilled this way; however, subsequent requests won't.

**isFetching**: When true, it shows that the Query is currently retrieving data, but it may already contain information from a previous request. This will be true for both the initial request sent and subsequent ones.

```
<div>
     {error && <p>Something went wrong</p>}
      {isFetching && <p>fetching ...</p>}
     {isLoading && <p>Loading ...</p>}
      {data?.map((todo) => (
 <Card key={todo.id} id={todo.id} todos={todo.body} />
      ))}
 </div>
```

_Mapping through the data to display todos_

<figure><img src="https://d37oebn0w9ir6a.cloudfront.net/account_32099/image2_286b87901df63098fd6efda612e7aefd.png" alt="The todos output."/><figcaption>_Displaying todos output_</figcaption></figure>

### Add Todo

To add a todo, we need to pass an object with the properties title, body and a unique id to the `addtodo` mutation endpoint that we implemented earlier.

Let's head over to our `Modal.js` file, where we’ll create a new todo:

<figure><img src="https://d37oebn0w9ir6a.cloudfront.net/account_32099/image1_e06c93d3dbb6016170e65e92380350b6.png" alt="The dialog to add a todo."/><figcaption>Adding a todo</figcaption></figure>

```
import { useAddTodoMutation } from "./Services/TodoApi";
```

```
const [addTodo, result] = useAddTodoMutation();
```

The mutation hook returns a tuple unlike that of the Query. The "trigger" function is the first item in the tuple, and an object with status, error, and data is the second item.

Now that we know that `addTask` is a trigger function, we need to pass in an object that has the properties we specified in the `addTask` endpoint:

```
//add a todo
 const addTodoHandler = () => {
    addTodo({
      title: todo.title,
      body: todo.body,
      id: Math.floor(Math.random() * 100),
    })
      .unwrap()
      .then((data) => {
 console.log(data);
      });
// force re-fetches the data
refetch()

};
```

The `unwrap()` function helps if you need to access the error or success payload immediately after a mutation. Wherever we refer to the `addTodoHandler` in our app, a new todo will be created. However, our UI won't be updated until the page is refreshed.

You can use the refetch method returned as a result property from a `useQuery` hook to obtain complete granular control over re-fetching data.

#### RTK Query Caching

[Tags](https://redux-toolkit.js.org/rtk-query/usage/automated-refetching#tags) is a term you can assign to a particular set of data in RTK Query to manage cache and invalidation behavior for re-fetching purposes. When determining if cached data should be affected by a mutation, it can be thought of as a "label" that is attached to the data and read after the change. The `providesTags` property for the query endpoint is used to provide the tag names to caches, and the `invalidatesTags` property for the mutation endpoint is used to remove them from caches.

### Update Todo

First, to update a todo, we have to import the mutation hook:

```
import {useUpdateTodoMutation} from"../../Services/TodoApi"

const [updateTodo] = useUpdateTodoMutation();
```

Destructuring our trigger function is done with the `updateTodo`**.** We call this when we want a particular todo to be edited by passing in the current todo to be updated:

```
const UpdateTodoHandler = () => {
    updateTodo({ id, ...todo })
      .unwrap()
      .then((data) => {
 console.log(data);
      })
      .catch((err) => {
 console.log(err);
      });
  };
```

### Delete Todo

Also, like our `updateTodo`, we have to import the mutation hook:

```
import {useDeleteTodoMutation } from "../../Services/TodoApi";
```

We also have to use our delete trigger function returned from the mutation hook:

```
const [deleteTodo] = useDeleteTodoMutation();
```

And we'd call it whenever we want to delete a particular todo, thereby passing the unique `id` of the todo to be deleted:

```
const DeleteTodoHandler = () => {

deleteTodo({id})
      .unwrap()
      .then((data) => {
 console.log("Todo deleted");
      })
      .catch((err) => {
 console.log(err);
      });
  };
```

## Conclusion

With a simple example like the one above where we perform CRUD operations on a JSON server, we display the obtained data in the UI depending on the request's state and cache the information in the form. Therefore, RTK Query not only uses an understandable API, but also significantly minimizes the amount of code written. The functionality of this program can always be expanded, and you can learn more about [RTK Query here](https://redux-toolkit.js.org/rtk-query/overview)**.**