RTK Query For Beginners
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
- Install
npm install @reduxjs/toolkit react-redux
- 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.