patterntypescriptreactTip
RTK Query — define an API slice and auto-generated hooks
Viewed 0 times
RTK QuerycreateApifetchBaseQueryprovidesTagsinvalidatesTagsauto-generated hookscache invalidation
Problem
Fetching data with Redux Toolkit means manually writing thunks, loading/error state, and cache logic. RTK Query is built into Redux Toolkit and eliminates this boilerplate, but developers unfamiliar with it continue writing createAsyncThunk manually.
Solution
Define an API slice with createApi and inject generated hooks into components:
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react';
interface Post { id: number; title: string; body: string; }
export const postsApi = createApi({
reducerPath: 'postsApi',
baseQuery: fetchBaseQuery({ baseUrl: '/api' }),
tagTypes: ['Post'],
endpoints: (builder) => ({
getPosts: builder.query<Post[], void>({
query: () => '/posts',
providesTags: ['Post'],
}),
getPostById: builder.query<Post, number>({
query: (id) =>
providesTags: (_result, _err, id) => [{ type: 'Post', id }],
}),
createPost: builder.mutation<Post, Omit<Post, 'id'>>({
query: (body) => ({ url: '/posts', method: 'POST', body }),
invalidatesTags: ['Post'],
}),
}),
});
export const { useGetPostsQuery, useGetPostByIdQuery, useCreatePostMutation } = postsApi;
// In component:
function PostList() {
const { data: posts, isLoading, isError } = useGetPostsQuery();
const [createPost, { isLoading: isCreating }] = useCreatePostMutation();
if (isLoading) return <Spinner />;
if (isError) return <ErrorBanner />;
return <ul>{posts?.map(p => <li key={p.id}>{p.title}</li>)}</ul>;
}
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react';
interface Post { id: number; title: string; body: string; }
export const postsApi = createApi({
reducerPath: 'postsApi',
baseQuery: fetchBaseQuery({ baseUrl: '/api' }),
tagTypes: ['Post'],
endpoints: (builder) => ({
getPosts: builder.query<Post[], void>({
query: () => '/posts',
providesTags: ['Post'],
}),
getPostById: builder.query<Post, number>({
query: (id) =>
/posts/${id},providesTags: (_result, _err, id) => [{ type: 'Post', id }],
}),
createPost: builder.mutation<Post, Omit<Post, 'id'>>({
query: (body) => ({ url: '/posts', method: 'POST', body }),
invalidatesTags: ['Post'],
}),
}),
});
export const { useGetPostsQuery, useGetPostByIdQuery, useCreatePostMutation } = postsApi;
// In component:
function PostList() {
const { data: posts, isLoading, isError } = useGetPostsQuery();
const [createPost, { isLoading: isCreating }] = useCreatePostMutation();
if (isLoading) return <Spinner />;
if (isError) return <ErrorBanner />;
return <ul>{posts?.map(p => <li key={p.id}>{p.title}</li>)}</ul>;
}
Why
RTK Query manages request deduplication, caching, background refetching, and cache invalidation automatically. The tag system links mutations to queries so stale data is refetched without manual dispatch calls.
Gotchas
- Add postsApi.reducer to configureStore and postsApi.middleware to the middleware array — forgetting the middleware breaks caching
- providesTags and invalidatesTags must use consistent tag type strings — typos silently break invalidation
- useQuery hooks re-subscribe to the cache; the same endpoint called from two components shares one network request
- RTK Query caches by endpoint + arg — queries with different args are cached independently
Revisions (0)
No revisions yet.