RTK Query For Beginners

Roman Sypchenko
4 min readApr 2, 2023

--

Discover the power of RTK Queries and learn how to leverage its key features for efficient data fetching, caching, and state management.

Introduction

In modern web applications, handling data fetching and state management effectively is crucial for building scalable and maintainable applications. Redux Toolkit Queries (RTK Queries) is a powerful solution that simplifies these tasks and offers a set of utilities to make your development process more efficient. In this article, we will explore the core features of RTK Queries, learn how to create endpoints and use auto-generated hooks, and understand how to work with direct calls without hooks.

When Should You Use RTK Query?

In general, the main reasons to use RTK Query are:

  • You already have a Redux app, and you want to simplify your existing data-fetching logic
  • You want to be able to use the Redux DevTools to see the history of changes to your state over time
  • You want to be able to integrate the RTK Query behavior with the rest of the Redux ecosystem
  • Your app logic needs to work outside of React

Key Features

  • Automatic caching
  • Background synchronization
  • Automatic retries
  • Pagination and load more support
  • Middleware integration

How to Use

  1. Install npm install @reduxjs/toolkit react-redux
  2. Create Endpoint
import { createApi, fetchBaseQuery } from "@reduxjs/toolkit/query/react";

const usersApi = createApi({
baseQuery: fetchBaseQuery({ baseUrl: "/users" }),
endpoints: (builder) => ({
fetchUser: builder.query({
query: (id) => `user/${id}`,
}),
createUser: builder.mutation({
query: (user) => ({
url: "user",
method: "POST",
body: user,
}),
}),
}),
});

export const { useFetchUserQuery, useCreateUserMutation } = usersApi;

2. Add the service to your store

import { configureStore } from '@reduxjs/toolkit'
// Or from '@reduxjs/toolkit/query/react'
import { setupListeners } from '@reduxjs/toolkit/query'
import { usersApi } from './services/usersApi'

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

// optional, but required for refetchOnFocus/refetchOnReconnect behaviors
// see `setupListeners` docs - takes an optional callback as the 2nd arg for customization
setupListeners(store.dispatch)

3. Wrap your application with the Provider

import * as React from 'react'
import { render } from 'react-dom'
import { Provider } from 'react-redux'

import App from './App'
import { store } from './app/store'

const rootElement = document.getElementById('root')
render(
<Provider store={store}>
<App />
</Provider>,
rootElement
)

How to use with Hooks

import React from "react";
import { useFetchUserQuery, useCreateUserMutation } from "./usersApi";

const UserProfile = ({ userId }) => {
const { data, error, isLoading } = useFetchUserQuery(userId);

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

return (
<div>
<h1>{data.name}</h1>
<p>Email: {data.email}</p>
</div>
);
};

const CreateUserForm = () => {
const [createUser, { isLoading }] = useCreateUserMutation();

const handleSubmit = async (user) => {
try {
await createUser(user).unwrap();
alert("User created successfully!");
} catch (error) {
alert(`Error: ${error.message}`);
}
};

// Render form components and handle form submission with handleSubmit
};

Read more about queries and mutations:

How to Use Direct Calls Without Hooks

import { usersApi } from "./usersApi";

const fetchUserById = async (id) => {
try {
const result = await usersApi.endpoints.fetchUser.initiate(id).unwrap();
console.log("User data:", result);
} catch (error) {
console.error("Error fetching user:", error);
}
};

fetchUserById(1);

Read more about using without hooks: https://redux-toolkit.js.org/rtk-query/usage/usage-without-react-hooks

How to use manual cache update

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

const api = createApi({
baseQuery: fetchBaseQuery({
baseUrl: '/',
}),
tagTypes: ['Post'],
endpoints: (build) => ({
getPost: build.query({
query: (id) => `post/${id}`,
providesTags: ['Post'],
}),
updatePost: build.mutation({
query: ({ id, ...patch }) => ({
url: `post/${id}`,
method: 'PATCH',
body: patch,
}),
async onQueryStarted({ id, ...patch }, { dispatch, queryFulfilled }) {
const patchResult = dispatch(
api.util.updateQueryData('getPost', id, (draft) => {
Object.assign(draft, patch)
})
)
try {
await queryFulfilled
} catch {
patchResult.undo()

/**
* Alternatively, on failure you can invalidate the corresponding cache tags
* to trigger a re-fetch:
* dispatch(api.util.invalidateTags(['Post']))
*/
}
},
}),
}),
})

Read more about manual updating cache here: https://redux-toolkit.js.org/rtk-query/usage/manual-cache-updates

Polling

import * as React from 'react'
import { useGetPokemonByNameQuery } from './services/pokemon'

export const Pokemon = ({ name }: { name: string }) => {
// Automatically refetch every 3s
const { data, status, error, refetch } = useGetPokemonByNameQuery(name, {
pollingInterval: 3000,
})

return <div>{data}</div>
}

Conclusion

Redux Toolkit Queries offers a robust and adaptable approach to enhancing your React applications' data fetching, caching, and state management. By mastering the creation of endpoints, leveraging auto-generated hooks, and utilizing direct calls without pins, you can unlock the full capabilities of RTK Queries and build more scalable and maintainable applications.

--

--