Part 4: Advanced Topics in RTK Query.
This part will focus on advanced features and use cases in RTK Query, including customising queries, handling authentication, optimistic updates, and performance optimisation.
Part 4: Advanced Topics in RTK Query
1. Introduction to Advanced RTK Query Concepts
In the previous part, we covered the basics of using RTK Query for fetching and mutating data. Now, we will dive into more advanced features that make RTK Query even more powerful. These features allow you to customize queries, manage authentication, optimize performance, and handle optimistic updates for a smoother user experience.
2. Customizing baseQuery
for Authentication
When working with APIs that require authentication, you need to customize the baseQuery
to include authentication headers like JWT tokens or API keys.
Step 1: Create a Custom baseQuery
You can create a custom baseQuery
function that adds authorization headers to every request.
// src/app/customBaseQuery.js
import { fetchBaseQuery } from '@reduxjs/toolkit/query/react';
const customBaseQuery = fetchBaseQuery({
baseUrl: 'https://jsonplaceholder.typicode.com/',
prepareHeaders: (headers, { getState }) => {
const token = getState().auth.token; // Assuming auth slice has token
if (token) {
headers.set('Authorization', `Bearer ${token}`);
}
return headers;
},
});
export default customBaseQuery;
Explanation:
-
prepareHeaders
: This function allows you to customize headers for each request. It retrieves the token from the Redux store and attaches it to theAuthorization
header.
Step 2: Use the Custom baseQuery
in createApi
Modify your postsApi.js
file to use the custom baseQuery
:
// src/features/posts/postsApi.js
import { createApi } from '@reduxjs/toolkit/query/react';
import customBaseQuery from '../../app/customBaseQuery';
export const postsApi = createApi({
reducerPath: 'postsApi',
baseQuery: customBaseQuery, // Use the custom base query here
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;
3. Optimistic Updates with RTK Query
Optimistic updates allow you to immediately update the UI before the server confirms the mutation, providing a smoother user experience. If the server returns an error, the UI can revert to the previous state.
Step 1: Implement Optimistic Updates in Mutations
You can implement optimistic updates using the onQueryStarted
lifecycle method provided by RTK Query.
// src/features/posts/postsApi.js
addPost: builder.mutation({
query: (newPost) => ({
url: 'posts',
method: 'POST',
body: newPost,
}),
invalidatesTags: ['Post'],
onQueryStarted: async (newPost, { dispatch, queryFulfilled }) => {
// Optimistic update: immediately add the new post to the cache
const patchResult = dispatch(
postsApi.util.updateQueryData('fetchPosts', undefined, (draftPosts) => {
draftPosts.push({ id: Date.now(), ...newPost }); // Fake ID for optimistic update
})
);
try {
await queryFulfilled; // Await server response
} catch {
patchResult.undo(); // Revert if the mutation fails
}
},
}),
Explanation:
-
onQueryStarted
: This lifecycle method is triggered when a mutation starts. It providesdispatch
andqueryFulfilled
parameters to manage cache updates. -
postsApi.util.updateQueryData
: This utility function allows you to optimistically update cached data. -
patchResult.undo()
: Reverts the optimistic update if the server returns an error.
4. Handling Dependent Queries
Sometimes, you may need to perform dependent queries, where one query depends on the result of another. RTK Query provides the skip
parameter to control when a query is executed.
Example: Fetch Post Details Based on Selected Post ID
// src/features/posts/PostDetails.js
import React from 'react';
import { useFetchPostQuery } from './postsApi';
const PostDetails = ({ postId }) => {
const { data: post, error, isLoading } = useFetchPostQuery(postId, { skip: !postId });
if (!postId) return <p>Select a post to view details.</p>;
if (isLoading) return <p>Loading...</p>;
if (error) return <p>Error loading post details.</p>;
return (
<div>
<h3>{post.title}</h3>
<p>{post.body}</p>
</div>
);
};
export default PostDetails;
Explanation:
-
useFetchPostQuery
: A query hook that takespostId
as an argument. IfpostId
is not provided, the query is skipped using{ skip: !postId }
.
5. Polling and Real-Time Data with RTK Query
RTK Query supports polling to keep data fresh at a specified interval. This is useful for real-time data synchronization.
Step 1: Use Polling in Queries
You can enable polling for any query using the pollingInterval
option.
// src/features/posts/PostsList.js
import React from 'react';
import { useFetchPostsQuery } from './postsApi';
const PostsList = () => {
const { data: posts, error, isLoading } = useFetchPostsQuery(undefined, {
pollingInterval: 30000, // Poll every 30 seconds
});
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;
Explanation:
-
pollingInterval
: This option specifies the interval (in milliseconds) at which the query should poll the server for new data.
6. Optimizing Performance with selectFromResult
RTK Query provides the selectFromResult
option for advanced performance optimizations by allowing you to select specific data from the query result.
Step 1: Using selectFromResult
to Optimize Re-Renders
The selectFromResult
option can be used to prevent unnecessary re-renders when only a subset of the query result is needed.
// src/features/posts/PostTitleList.js
import React from 'react';
import { useFetchPostsQuery } from './postsApi';
const PostTitleList = () => {
const { data: posts } = useFetchPostsQuery(undefined, {
selectFromResult: ({ data }) => ({ titles: data?.map((post) => post.title) }),
});
return (
<section>
<h2>Post Titles</h2>
<ul>
{posts?.map((title, index) => (
<li key={index}>{title}</li>
))}
</ul>
</section>
);
};
export default PostTitleList;
Explanation:
-
selectFromResult
: This option allows you to select only the titles from the fetched posts, preventing unnecessary re-renders when other data in the query result changes.
7. Conclusion and Next Steps
In this part, we explored advanced topics in RTK Query, such as customizing baseQuery
for authentication, handling optimistic updates, managing dependent queries, using polling for real-time data synchronization, and optimizing performance with selectFromResult
. RTK Query's rich feature set makes it a powerful tool for handling data fetching and caching in modern Redux applications.
In the next part, we'll discuss Testing Strategies for Redux Toolkit and RTK Query, covering unit testing, integration testing, and best practices for ensuring robust and maintainable code.
Stay tuned for Part 5: Testing Strategies for Redux Toolkit and RTK Query!
Top comments (0)