Advanced Redux Toolkit with Optimistic UI

Introduction

Redux Toolkit is a popular library for building efficient and scalable Redux applications. One of its advanced features is RTK Query, which provides a powerful and easy-to-use solution for fetching data from APIs. In addition to simplifying data fetching, RTK Query also provides advanced features like caching, pagination, and optimistic updates.

This blog post explores how to use RTK Query to implement optimistic UI updates in a React application. By the end of this tutorial, you will have a solid understanding of how to use RTK Query, how to manage a normalized cache state, and how to implement optimistic updates. You will also have a fully functional blog app with a simulated backend API.

Starter Code

To begin working with advanced Redux Toolkit and RTK Query, we must set up a new project with the required dependencies and boilerplate code. To save time and quickly get started, we can use a starter codebase that includes all the necessary files and configurations.

For this tutorial, I have created a GitHub repository that contains a starter codebase. The starter codebase includes a basic React application that has been configured with Redux Toolkit and RTK Query. Additionally, it includes a simple API that we will use to demonstrate optimistic UI updates.

You can find the GitHub repository here: https://github.com/Samuel46/redux-toolkit-starter

To use the starter code, simply clone the repository and install the dependencies:

<span><span style="color: var(--shiki-color-text)">git </span><span style="color: var(--shiki-token-string)">clone</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string)">https://github.com/Samuel46/redux-toolkit-starter</span></span>
<span><span style="color: var(--shiki-token-function)">cd</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string)">redux-toolkit-starter</span></span>
<span><span style="color: var(--shiki-color-text)">npm </span><span style="color: var(--shiki-token-string)">install</span></span>
<span></span>

Let's dive in!

Install and configure JSON Server to simulate a backend API

Next, we need to install JSON Server. JSON Server is a quick and easy way to simulate a backend API by serving data from a JSON file. To install JSON Server, open a new terminal window and navigate to the root directory of your project. Then, run the following command:

<span><span style="color: var(--shiki-color-text)">npm </span><span style="color: var(--shiki-token-string)">install</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string)">-g</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string)">json-server</span></span>
<span></span>

In the stater code, I have provided db.json which has users and 100 posts that will stimulate our API. After the JSON server is installed go ahead and run the following command to start the server:

<span><span style="color: var(--shiki-color-text)">json-server </span><span style="color: var(--shiki-token-string)">--watch</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string)">data/db.json</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string)">--port</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-constant)">8000</span></span>
<span></span>

Our server will run on port 8000, now we are making progress!!

Create an API slice with RTK Query

Great! Now that we have set up the JSON server to simulate a backend API, we can move on to the next step of creating an API slice with RTK Query for the posts resource.

To do this, we will first need to navigate to the src directory and then to the features folder. Inside the features folder, we will create a new folder called api. Inside this folder, we will create a new file called apiSlice.js.

In the apiSlice.js file, we will start by importing createApi and fetchBaseQuery from @reduxjs/toolkit/query/react. Next, we will define the apiSlice using the createApi function. This function takes an object with several properties including reducerPath, baseQuery, tagTypes, and endpoints.

<span><span style="color: var(--shiki-token-keyword)">import</span><span style="color: var(--shiki-color-text)"> { createApi</span><span style="color: var(--shiki-token-punctuation)">,</span><span style="color: var(--shiki-color-text)"> fetchBaseQuery } </span><span style="color: var(--shiki-token-keyword)">from</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string-expression)">&#39;@reduxjs/toolkit/query/react&#39;</span></span>
<span></span>
<span><span style="color: var(--shiki-token-keyword)">export</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">const</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-constant)">apiSlice</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">=</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-function)">createApi</span><span style="color: var(--shiki-color-text)">({</span></span>
<span><span style="color: var(--shiki-color-text)">  reducerPath</span><span style="color: var(--shiki-token-keyword)">:</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string-expression)">&#39;api&#39;</span><span style="color: var(--shiki-token-punctuation)">,</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-comment)">// optional</span></span>
<span><span style="color: var(--shiki-color-text)">  baseQuery</span><span style="color: var(--shiki-token-keyword)">:</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-function)">fetchBaseQuery</span><span style="color: var(--shiki-color-text)">({ baseUrl</span><span style="color: var(--shiki-token-keyword)">:</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string-expression)">&#39;http://localhost:8000&#39;</span><span style="color: var(--shiki-color-text)"> })</span><span style="color: var(--shiki-token-punctuation)">,</span></span>
<span><span style="color: var(--shiki-color-text)">  tagTypes</span><span style="color: var(--shiki-token-keyword)">:</span><span style="color: var(--shiki-color-text)"> [</span><span style="color: var(--shiki-token-string-expression)">&#39;Post&#39;</span><span style="color: var(--shiki-token-punctuation)">,</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string-expression)">&#39;User&#39;</span><span style="color: var(--shiki-color-text)">]</span><span style="color: var(--shiki-token-punctuation)">,</span></span>
<span><span style="color: var(--shiki-color-text)">  </span><span style="color: var(--shiki-token-function)">endpoints</span><span style="color: var(--shiki-token-keyword)">:</span><span style="color: var(--shiki-color-text)"> (builder) </span><span style="color: var(--shiki-token-keyword)">=&gt;</span><span style="color: var(--shiki-color-text)"> ({})</span><span style="color: var(--shiki-token-punctuation)">,</span></span>
<span><span style="color: var(--shiki-color-text)">})</span></span>
<span></span>
  • Name
    endpoints
    Type
    builders
    Description

    The endpoints property is a function that takes a builder object as an argument. The builder object should be empty since we will export the builder in other slices. If not, it will not work.

  • Name
    tagTypes
    Type
    Post, User
    Description

    The tagTypes property is an array of strings that will be used to tag endpoints. This is useful for caching and invalidation.

  • Name
    baseQuery
    Type
    baseUrl
    Description

    The baseQuery property is a function that takes an object with a baseUrl property as an argument. The baseUrl property is the base URL of the API that we want to fetch data from.

  • Name
    reducerPath
    Type
    api
    Description

    The reducerPath is optional when we name it api, it will be used as the prefix for the generated action types.

Update the postsSlice to an extended API slice

Navigate to the postsSlice.js file located at src/features/posts/postsSlice.js. In this file, we will import the apiSlice we created in the previous step. Then, we will use the builder object to create endpoints for creating, updating, and deleting posts. Additionally, we will create an endpoint for fetching a single post and a list of posts.

<span><span style="color: var(--shiki-token-keyword)">import</span><span style="color: var(--shiki-color-text)"> { createSelector</span><span style="color: var(--shiki-token-punctuation)">,</span><span style="color: var(--shiki-color-text)"> createEntityAdapter } </span><span style="color: var(--shiki-token-keyword)">from</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string-expression)">&#39;@reduxjs/toolkit&#39;</span></span>
<span><span style="color: var(--shiki-token-keyword)">import</span><span style="color: var(--shiki-color-text)"> { apiSlice } </span><span style="color: var(--shiki-token-keyword)">from</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string-expression)">&#39;../api/apiSlice&#39;</span></span>
<span></span>

Next, we define the posts adapter to normalize our data by sorting it into entities and IDs in descending order.

<span><span style="color: var(--shiki-token-keyword)">const</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-constant)">postsAdapter</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">=</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-function)">createEntityAdapter</span><span style="color: var(--shiki-color-text)">({</span></span>
<span><span style="color: var(--shiki-color-text)">  </span><span style="color: var(--shiki-token-function)">sortComparer</span><span style="color: var(--shiki-token-keyword)">:</span><span style="color: var(--shiki-color-text)"> (a</span><span style="color: var(--shiki-token-punctuation)">,</span><span style="color: var(--shiki-color-text)"> b) </span><span style="color: var(--shiki-token-keyword)">=&gt;</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-constant)">b</span><span style="color: var(--shiki-token-function)">.</span><span style="color: var(--shiki-token-constant)">date</span><span style="color: var(--shiki-token-function)">.localeCompare</span><span style="color: var(--shiki-color-text)">(</span><span style="color: var(--shiki-token-constant)">a</span><span style="color: var(--shiki-color-text)">.date)</span><span style="color: var(--shiki-token-punctuation)">,</span></span>
<span><span style="color: var(--shiki-color-text)">})</span></span>
<span></span>

Next, initialize the initial state of the posts slice, passing in the postsAdapter as an argument. This will return an object with the following properties:

  • Name
    entities
    Type
    Description

    An object where each key is an entity ID, and each value is the entity object

  • Name
    ids
    Type
    Description

    The tagTypes property is an array of strings that will be used to tag endpoints. This is useful for caching and invalidation.

Implement the getPosts endpoint method

<span><span style="color: var(--shiki-token-keyword)">const</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-constant)">initialState</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">=</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-constant)">postsAdapter</span><span style="color: var(--shiki-token-function)">.getInitialState</span><span style="color: var(--shiki-color-text)">()</span></span>
<span></span>

Once this is set up, we can begin working on our endpoints. We will create a getPosts builder that will have the following properties:

  • Name
    query:
    Type
    Description

    The query property is a function that takes no arguments and returns a string that represents the URL of the endpoint.
    In this case, we are returning the URL of the posts resource. query: () => '/posts'

  • Name
    transformResponse
    Type
    Description

    The transformResponse property is a function that takes the response data as an argument and returns the transformed data. We use it for two reasons:

    1. To add reactions and dates, since the original data does not have these properties.
    2. To normalize our state by using postsAdapter to convert the data into a normalized format. This will result in an ids array and an entities object, which we can use to look up our posts.
  • Name
    providesTags
    Type
    Description

    An array of tags is used to tag the endpoint for caching and invalidation. In this case, we tag the endpoint with the "Post" tag type and the "LIST" id, which invalidates the cache when a new post is added.

    To provide a separate object for each post, we use the "ids" array and map over it. Each object returned has the "Post" tag type and the corresponding post id. When we invalidate the id, the whole list will be refetched.

<span><span style="color: var(--shiki-token-keyword)">export</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">const</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-constant)">extendedApiSlice</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">=</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-constant)">apiSlice</span><span style="color: var(--shiki-token-function)">.injectEndpoints</span><span style="color: var(--shiki-color-text)">({</span></span>
<span><span style="color: var(--shiki-color-text)">  </span><span style="color: var(--shiki-token-function)">endpoints</span><span style="color: var(--shiki-token-keyword)">:</span><span style="color: var(--shiki-color-text)"> (builder) </span><span style="color: var(--shiki-token-keyword)">=&gt;</span><span style="color: var(--shiki-color-text)"> ({</span></span>
<span><span style="color: var(--shiki-color-text)">    getPosts</span><span style="color: var(--shiki-token-keyword)">:</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-constant)">builder</span><span style="color: var(--shiki-token-function)">.query</span><span style="color: var(--shiki-color-text)">({</span></span>
<span><span style="color: var(--shiki-color-text)">      </span><span style="color: var(--shiki-token-function)">query</span><span style="color: var(--shiki-token-keyword)">:</span><span style="color: var(--shiki-color-text)"> () </span><span style="color: var(--shiki-token-keyword)">=&gt;</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string-expression)">&#39;/posts&#39;</span><span style="color: var(--shiki-token-punctuation)">,</span></span>
<span><span style="color: var(--shiki-color-text)">      </span><span style="color: var(--shiki-token-function)">transformResponse</span><span style="color: var(--shiki-token-keyword)">:</span><span style="color: var(--shiki-color-text)"> (responseData) </span><span style="color: var(--shiki-token-keyword)">=&gt;</span><span style="color: var(--shiki-color-text)"> {</span></span>
<span><span style="color: var(--shiki-color-text)">        </span><span style="color: var(--shiki-token-keyword)">let</span><span style="color: var(--shiki-color-text)"> min </span><span style="color: var(--shiki-token-keyword)">=</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-constant)">1</span></span>
<span><span style="color: var(--shiki-color-text)">        </span><span style="color: var(--shiki-token-keyword)">const</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-constant)">loadedPosts</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">=</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-constant)">responseData</span><span style="color: var(--shiki-token-function)">.map</span><span style="color: var(--shiki-color-text)">((post) </span><span style="color: var(--shiki-token-keyword)">=&gt;</span><span style="color: var(--shiki-color-text)"> {</span></span>
<span><span style="color: var(--shiki-color-text)">          </span><span style="color: var(--shiki-token-keyword)">if</span><span style="color: var(--shiki-color-text)"> (</span><span style="color: var(--shiki-token-keyword)">!</span><span style="color: var(--shiki-token-constant)">post</span><span style="color: var(--shiki-color-text)">?.date)</span></span>
<span><span style="color: var(--shiki-color-text)">            </span><span style="color: var(--shiki-token-constant)">post</span><span style="color: var(--shiki-color-text)">.date </span><span style="color: var(--shiki-token-keyword)">=</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-function)">sub</span><span style="color: var(--shiki-color-text)">(</span><span style="color: var(--shiki-token-keyword)">new</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-function)">Date</span><span style="color: var(--shiki-color-text)">()</span><span style="color: var(--shiki-token-punctuation)">,</span><span style="color: var(--shiki-color-text)"> { minutes</span><span style="color: var(--shiki-token-keyword)">:</span><span style="color: var(--shiki-color-text)"> min</span><span style="color: var(--shiki-token-keyword)">++</span><span style="color: var(--shiki-color-text)"> })</span><span style="color: var(--shiki-token-function)">.toISOString</span><span style="color: var(--shiki-color-text)">()</span></span>
<span><span style="color: var(--shiki-color-text)">          </span><span style="color: var(--shiki-token-keyword)">if</span><span style="color: var(--shiki-color-text)"> (</span><span style="color: var(--shiki-token-keyword)">!</span><span style="color: var(--shiki-token-constant)">post</span><span style="color: var(--shiki-color-text)">?.reactions)</span></span>
<span><span style="color: var(--shiki-color-text)">            </span><span style="color: var(--shiki-token-constant)">post</span><span style="color: var(--shiki-color-text)">.reactions </span><span style="color: var(--shiki-token-keyword)">=</span><span style="color: var(--shiki-color-text)"> {</span></span>
<span><span style="color: var(--shiki-color-text)">              thumbsUp</span><span style="color: var(--shiki-token-keyword)">:</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-constant)">0</span><span style="color: var(--shiki-token-punctuation)">,</span></span>
<span><span style="color: var(--shiki-color-text)">              wow</span><span style="color: var(--shiki-token-keyword)">:</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-constant)">0</span><span style="color: var(--shiki-token-punctuation)">,</span></span>
<span><span style="color: var(--shiki-color-text)">              heart</span><span style="color: var(--shiki-token-keyword)">:</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-constant)">0</span><span style="color: var(--shiki-token-punctuation)">,</span></span>
<span><span style="color: var(--shiki-color-text)">              rocket</span><span style="color: var(--shiki-token-keyword)">:</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-constant)">0</span><span style="color: var(--shiki-token-punctuation)">,</span></span>
<span><span style="color: var(--shiki-color-text)">              coffee</span><span style="color: var(--shiki-token-keyword)">:</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-constant)">0</span><span style="color: var(--shiki-token-punctuation)">,</span></span>
<span><span style="color: var(--shiki-color-text)">            }</span></span>
<span><span style="color: var(--shiki-color-text)">          </span><span style="color: var(--shiki-token-keyword)">return</span><span style="color: var(--shiki-color-text)"> post</span></span>
<span><span style="color: var(--shiki-color-text)">        })</span></span>
<span><span style="color: var(--shiki-color-text)">        </span><span style="color: var(--shiki-token-keyword)">return</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-constant)">postsAdapter</span><span style="color: var(--shiki-token-function)">.setAll</span><span style="color: var(--shiki-color-text)">(initialState</span><span style="color: var(--shiki-token-punctuation)">,</span><span style="color: var(--shiki-color-text)"> loadedPosts)</span></span>
<span><span style="color: var(--shiki-color-text)">      }</span><span style="color: var(--shiki-token-punctuation)">,</span></span>
<span><span style="color: var(--shiki-color-text)">      </span><span style="color: var(--shiki-token-function)">providesTags</span><span style="color: var(--shiki-token-keyword)">:</span><span style="color: var(--shiki-color-text)"> (result</span><span style="color: var(--shiki-token-punctuation)">,</span><span style="color: var(--shiki-color-text)"> error</span><span style="color: var(--shiki-token-punctuation)">,</span><span style="color: var(--shiki-color-text)"> arg) </span><span style="color: var(--shiki-token-keyword)">=&gt;</span><span style="color: var(--shiki-color-text)"> [</span></span>
<span><span style="color: var(--shiki-color-text)">        { type</span><span style="color: var(--shiki-token-keyword)">:</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string-expression)">&#39;Post&#39;</span><span style="color: var(--shiki-token-punctuation)">,</span><span style="color: var(--shiki-color-text)"> id</span><span style="color: var(--shiki-token-keyword)">:</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string-expression)">&#39;LIST&#39;</span><span style="color: var(--shiki-color-text)"> }</span><span style="color: var(--shiki-token-punctuation)">,</span></span>
<span><span style="color: var(--shiki-color-text)">        </span><span style="color: var(--shiki-token-keyword)">...</span><span style="color: var(--shiki-token-constant)">result</span><span style="color: var(--shiki-token-function)">.</span><span style="color: var(--shiki-token-constant)">ids</span><span style="color: var(--shiki-token-function)">.map</span><span style="color: var(--shiki-color-text)">((id) </span><span style="color: var(--shiki-token-keyword)">=&gt;</span><span style="color: var(--shiki-color-text)"> ({ type</span><span style="color: var(--shiki-token-keyword)">:</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string-expression)">&#39;Post&#39;</span><span style="color: var(--shiki-token-punctuation)">,</span><span style="color: var(--shiki-color-text)"> id }))</span><span style="color: var(--shiki-token-punctuation)">,</span></span>
<span><span style="color: var(--shiki-color-text)">      ]</span><span style="color: var(--shiki-token-punctuation)">,</span></span>
<span><span style="color: var(--shiki-color-text)">    })</span><span style="color: var(--shiki-token-punctuation)">,</span></span>
<span><span style="color: var(--shiki-color-text)">  })</span><span style="color: var(--shiki-token-punctuation)">,</span></span>
<span><span style="color: var(--shiki-color-text)">})</span></span>
<span></span>

RTK query will automatically generate custom hooks based on the methods passed above, In this case we will have useGetPostsQuery generate for us!

<span><span style="color: var(--shiki-token-keyword)">export</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">const</span><span style="color: var(--shiki-color-text)"> { </span><span style="color: var(--shiki-token-constant)">useGetPostsQuery</span><span style="color: var(--shiki-color-text)"> } </span><span style="color: var(--shiki-token-keyword)">=</span><span style="color: var(--shiki-color-text)"> extendedApiSlice</span></span>
<span></span>

Update the postsSlice selectors

Let's break down the purpose of each selector:

selectPostsResult: Returns the query result object, which is the data received from the server after requesting for posts. selectPostsData: Returns the normalized state object containing ids and entities of the posts. selectAllPosts: Returns all posts in the state. selectPostById: Returns a single post by its id. selectPostIds: Returns an array of post ids. To modify the getSelectors method, we need to pass the selectPostsData selector as an argument. This is necessary because getSelectors creates selectors that return the posts slice of the state. Additionally, we need to add the nullish coalescing operator ?? to the selectPostsData selector to return the initial state if it returns undefined.

<span><span style="color: var(--shiki-token-comment)">// returns the query result object</span></span>
<span><span style="color: var(--shiki-token-keyword)">export</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">const</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-constant)">selectPostsResult</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">=</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-constant)">extendedApiSlice</span><span style="color: var(--shiki-token-function)">.</span><span style="color: var(--shiki-token-constant)">endpoints</span><span style="color: var(--shiki-token-function)">.</span><span style="color: var(--shiki-token-constant)">getPosts</span><span style="color: var(--shiki-token-function)">.select</span><span style="color: var(--shiki-color-text)">()</span></span>
<span></span>
<span><span style="color: var(--shiki-token-comment)">// Creates memoized selector</span></span>
<span><span style="color: var(--shiki-token-keyword)">const</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-constant)">selectPostsData</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">=</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-function)">createSelector</span><span style="color: var(--shiki-color-text)">(</span></span>
<span><span style="color: var(--shiki-color-text)">  selectPostsResult</span><span style="color: var(--shiki-token-punctuation)">,</span></span>
<span><span style="color: var(--shiki-color-text)">  (postsResult) </span><span style="color: var(--shiki-token-keyword)">=&gt;</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-constant)">postsResult</span><span style="color: var(--shiki-color-text)">.data </span><span style="color: var(--shiki-token-comment)">// normalized state object with ids &amp; entities</span></span>
<span><span style="color: var(--shiki-color-text)">)</span></span>
<span></span>
<span><span style="color: var(--shiki-token-comment)">//getSelectors creates these selectors and we rename them with aliases using destructuring</span></span>
<span><span style="color: var(--shiki-token-keyword)">export</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">const</span><span style="color: var(--shiki-color-text)"> {</span></span>
<span><span style="color: var(--shiki-color-text)">  selectAll: </span><span style="color: var(--shiki-token-constant)">selectAllPosts</span><span style="color: var(--shiki-token-punctuation)">,</span></span>
<span><span style="color: var(--shiki-color-text)">  selectById: </span><span style="color: var(--shiki-token-constant)">selectPostById</span><span style="color: var(--shiki-token-punctuation)">,</span></span>
<span><span style="color: var(--shiki-color-text)">  selectIds: </span><span style="color: var(--shiki-token-constant)">selectPostIds</span><span style="color: var(--shiki-token-punctuation)">,</span></span>
<span><span style="color: var(--shiki-color-text)">  </span><span style="color: var(--shiki-token-comment)">// Pass in a selector that returns the posts slice of state</span></span>
<span><span style="color: var(--shiki-color-text)">} </span><span style="color: var(--shiki-token-keyword)">=</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-constant)">postsAdapter</span><span style="color: var(--shiki-token-function)">.getSelectors</span><span style="color: var(--shiki-color-text)">((state) </span><span style="color: var(--shiki-token-keyword)">=&gt;</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-function)">selectPostsData</span><span style="color: var(--shiki-color-text)">(state) </span><span style="color: var(--shiki-token-keyword)">??</span><span style="color: var(--shiki-color-text)"> initialState)</span></span>
<span></span>

Reconfigure the store to use the new API slice

Inside the app folder, we have a file called store.js. We'll import the apiSlice and add it to the middleware array. Additionally, we'll add the apiSlice.reducer to the configureStore method. getDefaultMiddleware is a required function in Redux Toolkit as it manages cache lifetime and expiration. We must use it when utilizing RTQ query with api slice.

<span><span style="color: var(--shiki-token-keyword)">import</span><span style="color: var(--shiki-color-text)"> { configureStore } </span><span style="color: var(--shiki-token-keyword)">from</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string-expression)">&#39;@reduxjs/toolkit&#39;</span></span>
<span><span style="color: var(--shiki-token-keyword)">import</span><span style="color: var(--shiki-color-text)"> { apiSlice } </span><span style="color: var(--shiki-token-keyword)">from</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string-expression)">&#39;../features/api/apiSlice&#39;</span></span>
<span></span>
<span><span style="color: var(--shiki-token-keyword)">export</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">const</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-constant)">store</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">=</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-function)">configureStore</span><span style="color: var(--shiki-color-text)">({</span></span>
<span><span style="color: var(--shiki-color-text)">  reducer</span><span style="color: var(--shiki-token-keyword)">:</span><span style="color: var(--shiki-color-text)"> {</span></span>
<span><span style="color: var(--shiki-color-text)">    [</span><span style="color: var(--shiki-token-constant)">apiSlice</span><span style="color: var(--shiki-color-text)">.reducerPath]</span><span style="color: var(--shiki-token-keyword)">:</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-constant)">apiSlice</span><span style="color: var(--shiki-color-text)">.reducer</span><span style="color: var(--shiki-token-punctuation)">,</span></span>
<span><span style="color: var(--shiki-color-text)">  }</span><span style="color: var(--shiki-token-punctuation)">,</span></span>
<span><span style="color: var(--shiki-color-text)">  </span><span style="color: var(--shiki-token-function)">middleware</span><span style="color: var(--shiki-token-keyword)">:</span><span style="color: var(--shiki-color-text)"> (getDefaultMiddleware) </span><span style="color: var(--shiki-token-keyword)">=&gt;</span></span>
<span><span style="color: var(--shiki-color-text)">    </span><span style="color: var(--shiki-token-function)">getDefaultMiddleware</span><span style="color: var(--shiki-color-text)">()</span><span style="color: var(--shiki-token-function)">.concat</span><span style="color: var(--shiki-color-text)">(</span><span style="color: var(--shiki-token-constant)">apiSlice</span><span style="color: var(--shiki-color-text)">.middleware)</span><span style="color: var(--shiki-token-punctuation)">,</span></span>
<span><span style="color: var(--shiki-color-text)">  devTools</span><span style="color: var(--shiki-token-keyword)">:</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-constant)">true</span><span style="color: var(--shiki-token-punctuation)">,</span></span>
<span><span style="color: var(--shiki-color-text)">})</span></span>
<span></span>

Update index.js to use the new store

In the index.js file, we'll import the store and wrap the app with the store provider. Next, we'll bring in the extendedApiSlice and dispatch the getPosts method, which fetches all the posts from the server and stores them in the newly created store. This occurs when the app is initialized for the first time.

<span><span style="color: var(--shiki-token-keyword)">import</span><span style="color: var(--shiki-color-text)"> React </span><span style="color: var(--shiki-token-keyword)">from</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string-expression)">&#39;react&#39;</span></span>
<span><span style="color: var(--shiki-token-keyword)">import</span><span style="color: var(--shiki-color-text)"> ReactDOM </span><span style="color: var(--shiki-token-keyword)">from</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string-expression)">&#39;react-dom&#39;</span></span>
<span><span style="color: var(--shiki-token-keyword)">import</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string-expression)">&#39;./index.css&#39;</span></span>
<span><span style="color: var(--shiki-token-keyword)">import</span><span style="color: var(--shiki-color-text)"> App </span><span style="color: var(--shiki-token-keyword)">from</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string-expression)">&#39;./App&#39;</span></span>
<span><span style="color: var(--shiki-token-keyword)">import</span><span style="color: var(--shiki-color-text)"> { store } </span><span style="color: var(--shiki-token-keyword)">from</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string-expression)">&#39;./app/store&#39;</span></span>
<span><span style="color: var(--shiki-token-keyword)">import</span><span style="color: var(--shiki-color-text)"> { Provider } </span><span style="color: var(--shiki-token-keyword)">from</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string-expression)">&#39;react-redux&#39;</span></span>
<span><span style="color: var(--shiki-token-keyword)">import</span><span style="color: var(--shiki-color-text)"> { extendedApiSlice } </span><span style="color: var(--shiki-token-keyword)">from</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string-expression)">&#39;./features/posts/postsSlice&#39;</span></span>
<span><span style="color: var(--shiki-token-keyword)">import</span><span style="color: var(--shiki-color-text)"> { BrowserRouter </span><span style="color: var(--shiki-token-keyword)">as</span><span style="color: var(--shiki-color-text)"> Router</span><span style="color: var(--shiki-token-punctuation)">,</span><span style="color: var(--shiki-color-text)"> Routes</span><span style="color: var(--shiki-token-punctuation)">,</span><span style="color: var(--shiki-color-text)"> Route } </span><span style="color: var(--shiki-token-keyword)">from</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string-expression)">&#39;react-router-dom&#39;</span></span>
<span></span>
<span><span style="color: var(--shiki-token-comment)">// This will fetch the posts from our fake server and store them in the store</span></span>
<span><span style="color: var(--shiki-token-constant)">store</span><span style="color: var(--shiki-token-function)">.dispatch</span><span style="color: var(--shiki-color-text)">(</span><span style="color: var(--shiki-token-constant)">extendedApiSlice</span><span style="color: var(--shiki-token-function)">.</span><span style="color: var(--shiki-token-constant)">endpoints</span><span style="color: var(--shiki-token-function)">.</span><span style="color: var(--shiki-token-constant)">getPosts</span><span style="color: var(--shiki-token-function)">.initiate</span><span style="color: var(--shiki-color-text)">())</span></span>
<span></span>
<span><span style="color: var(--shiki-token-constant)">ReactDOM</span><span style="color: var(--shiki-token-function)">.render</span><span style="color: var(--shiki-color-text)">(</span></span>
<span><span style="color: var(--shiki-color-text)">  &lt;</span><span style="color: var(--shiki-token-constant)">React.StrictMode</span><span style="color: var(--shiki-color-text)">&gt;</span></span>
<span><span style="color: var(--shiki-color-text)">    {</span><span style="color: var(--shiki-token-comment)">/* wrap the app with store we just created! */</span><span style="color: var(--shiki-color-text)">}</span></span>
<span><span style="color: var(--shiki-color-text)">    &lt;</span><span style="color: var(--shiki-token-constant)">Provider</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-function)">store</span><span style="color: var(--shiki-token-keyword)">=</span><span style="color: var(--shiki-color-text)">{store}&gt;</span></span>
<span><span style="color: var(--shiki-color-text)">      &lt;</span><span style="color: var(--shiki-token-constant)">Router</span><span style="color: var(--shiki-color-text)">&gt;</span></span>
<span><span style="color: var(--shiki-color-text)">        &lt;</span><span style="color: var(--shiki-token-constant)">Routes</span><span style="color: var(--shiki-color-text)">&gt;</span></span>
<span><span style="color: var(--shiki-color-text)">          &lt;</span><span style="color: var(--shiki-token-constant)">Route</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-function)">path</span><span style="color: var(--shiki-token-keyword)">=</span><span style="color: var(--shiki-token-string-expression)">&quot;/*&quot;</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-function)">element</span><span style="color: var(--shiki-token-keyword)">=</span><span style="color: var(--shiki-color-text)">{&lt;</span><span style="color: var(--shiki-token-constant)">App</span><span style="color: var(--shiki-color-text)"> /&gt;} /&gt;</span></span>
<span><span style="color: var(--shiki-color-text)">        &lt;/</span><span style="color: var(--shiki-token-constant)">Routes</span><span style="color: var(--shiki-color-text)">&gt;</span></span>
<span><span style="color: var(--shiki-color-text)">      &lt;/</span><span style="color: var(--shiki-token-constant)">Router</span><span style="color: var(--shiki-color-text)">&gt;</span></span>
<span><span style="color: var(--shiki-color-text)">    &lt;/</span><span style="color: var(--shiki-token-constant)">Provider</span><span style="color: var(--shiki-color-text)">&gt;</span></span>
<span><span style="color: var(--shiki-color-text)">  &lt;/</span><span style="color: var(--shiki-token-constant)">React.StrictMode</span><span style="color: var(--shiki-color-text)">&gt;</span><span style="color: var(--shiki-token-punctuation)">,</span></span>
<span><span style="color: var(--shiki-color-text)">  </span><span style="color: var(--shiki-token-constant)">document</span><span style="color: var(--shiki-token-function)">.getElementById</span><span style="color: var(--shiki-color-text)">(</span><span style="color: var(--shiki-token-string-expression)">&#39;root&#39;</span><span style="color: var(--shiki-color-text)">)</span></span>
<span><span style="color: var(--shiki-color-text)">)</span></span>
<span></span>

Implement the PostsList component

We'll now move over to the PostsList component to utilize the useGetPostQuery generated from our postSlice. At the top, let's import the useGetPostsQuery hook and the selectAllPosts selector. We'll use the useGetPostsQuery hook to fetch the posts from the server and the selectAllPosts selector to select all posts from the store.

The useGetPostsQuery hook returns an object with the following properties:

  • Name
    isLoading
    Type
    Description

    This is a boolean that will be true while the query is in the pending state.

  • Name
    isSuccess
    Type
    Description

    This is a boolean that will be true when the query is in the fulfilled state.

  • Name
    isError
    Type
    Description

    This is a boolean that will be true when the query is in the rejected state.

We'll utilize the isLoading, isSuccess, and isError properties to display loading, error, and success states. Additionally, we'll use the error property to display any error messages. Next, we'll use the orderedPostIds selector to obtain an array of post ids. We'll map over the array and render a PostsExcerpt component for each post.

<span><span style="color: var(--shiki-token-keyword)">import</span><span style="color: var(--shiki-color-text)"> { useSelector } </span><span style="color: var(--shiki-token-keyword)">from</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string-expression)">&#39;react-redux&#39;</span></span>
<span><span style="color: var(--shiki-token-keyword)">import</span><span style="color: var(--shiki-color-text)"> { selectPostIds } </span><span style="color: var(--shiki-token-keyword)">from</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string-expression)">&#39;./postsSlice&#39;</span></span>
<span><span style="color: var(--shiki-token-keyword)">import</span><span style="color: var(--shiki-color-text)"> PostsExcerpt </span><span style="color: var(--shiki-token-keyword)">from</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string-expression)">&#39;./PostsExcerpt&#39;</span></span>
<span><span style="color: var(--shiki-token-comment)">// Bring in the useGetPostsQuery hook from the postsSlice</span></span>
<span><span style="color: var(--shiki-token-keyword)">import</span><span style="color: var(--shiki-color-text)"> { useGetPostsQuery } </span><span style="color: var(--shiki-token-keyword)">from</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string-expression)">&#39;./postsSlice&#39;</span></span>
<span></span>
<span><span style="color: var(--shiki-token-keyword)">const</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-function)">PostsList</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">=</span><span style="color: var(--shiki-color-text)"> () </span><span style="color: var(--shiki-token-keyword)">=&gt;</span><span style="color: var(--shiki-color-text)"> {</span></span>
<span><span style="color: var(--shiki-color-text)">  </span><span style="color: var(--shiki-token-keyword)">const</span><span style="color: var(--shiki-color-text)"> { </span><span style="color: var(--shiki-token-constant)">isLoading</span><span style="color: var(--shiki-token-punctuation)">,</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-constant)">isSuccess</span><span style="color: var(--shiki-token-punctuation)">,</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-constant)">isError</span><span style="color: var(--shiki-token-punctuation)">,</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-constant)">error</span><span style="color: var(--shiki-color-text)"> } </span><span style="color: var(--shiki-token-keyword)">=</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-function)">useGetPostsQuery</span><span style="color: var(--shiki-color-text)">()</span></span>
<span></span>
<span><span style="color: var(--shiki-color-text)">  </span><span style="color: var(--shiki-token-keyword)">const</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-constant)">orderedPostIds</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">=</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-function)">useSelector</span><span style="color: var(--shiki-color-text)">(selectPostIds)</span></span>
<span></span>
<span><span style="color: var(--shiki-color-text)">  </span><span style="color: var(--shiki-token-keyword)">let</span><span style="color: var(--shiki-color-text)"> content</span></span>
<span><span style="color: var(--shiki-color-text)">  </span><span style="color: var(--shiki-token-keyword)">if</span><span style="color: var(--shiki-color-text)"> (isLoading) {</span></span>
<span><span style="color: var(--shiki-color-text)">    content </span><span style="color: var(--shiki-token-keyword)">=</span><span style="color: var(--shiki-color-text)"> &lt;</span><span style="color: var(--shiki-token-string-expression)">p</span><span style="color: var(--shiki-color-text)">&gt;&quot;Loading...&quot;&lt;/</span><span style="color: var(--shiki-token-string-expression)">p</span><span style="color: var(--shiki-color-text)">&gt;</span></span>
<span><span style="color: var(--shiki-color-text)">  } </span><span style="color: var(--shiki-token-keyword)">else</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">if</span><span style="color: var(--shiki-color-text)"> (isSuccess) {</span></span>
<span><span style="color: var(--shiki-color-text)">    content </span><span style="color: var(--shiki-token-keyword)">=</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-constant)">orderedPostIds</span><span style="color: var(--shiki-token-function)">.map</span><span style="color: var(--shiki-color-text)">((postId) </span><span style="color: var(--shiki-token-keyword)">=&gt;</span><span style="color: var(--shiki-color-text)"> (</span></span>
<span><span style="color: var(--shiki-color-text)">      &lt;</span><span style="color: var(--shiki-token-constant)">PostsExcerpt</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-function)">key</span><span style="color: var(--shiki-token-keyword)">=</span><span style="color: var(--shiki-color-text)">{postId} </span><span style="color: var(--shiki-token-function)">postId</span><span style="color: var(--shiki-token-keyword)">=</span><span style="color: var(--shiki-color-text)">{postId} /&gt;</span></span>
<span><span style="color: var(--shiki-color-text)">    ))</span></span>
<span><span style="color: var(--shiki-color-text)">  } </span><span style="color: var(--shiki-token-keyword)">else</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">if</span><span style="color: var(--shiki-color-text)"> (isError) {</span></span>
<span><span style="color: var(--shiki-color-text)">    content </span><span style="color: var(--shiki-token-keyword)">=</span><span style="color: var(--shiki-color-text)"> &lt;</span><span style="color: var(--shiki-token-string-expression)">p</span><span style="color: var(--shiki-color-text)">&gt;{error}&lt;/</span><span style="color: var(--shiki-token-string-expression)">p</span><span style="color: var(--shiki-color-text)">&gt;</span></span>
<span><span style="color: var(--shiki-color-text)">  }</span></span>
<span></span>
<span><span style="color: var(--shiki-color-text)">  </span><span style="color: var(--shiki-token-keyword)">return</span><span style="color: var(--shiki-color-text)"> &lt;</span><span style="color: var(--shiki-token-string-expression)">section</span><span style="color: var(--shiki-color-text)">&gt;{content}&lt;/</span><span style="color: var(--shiki-token-string-expression)">section</span><span style="color: var(--shiki-color-text)">&gt;</span></span>
<span><span style="color: var(--shiki-color-text)">}</span></span>
<span><span style="color: var(--shiki-token-keyword)">export</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">default</span><span style="color: var(--shiki-color-text)"> PostsList</span></span>
<span></span>

Implement the getPostsByUserId endpoint method

Let's proceed to the postSlice and add a new method to handle the fetching of posts by userID.

Our query will receive the id value attached to the request via the params property. We will use transformResponse, similar to the getPosts method, to normalize our data. This will not overwrite the cache state of the request, as Redux is subscribing to these different queries, which we can see in the Redux DevTools.

For providesTags, we are providing an object with Post and id, which means we can invalidate the cache of each individual post by using the ID provided by the ids array from the postsAdapter.

Remember, each method we add creates a custom hook. Let's export the useGetPostsByUserIdQuery hook!

<span><span style="color: var(--shiki-token-keyword)">export</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">const</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-constant)">extendedApiSlice</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">=</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-constant)">apiSlice</span><span style="color: var(--shiki-token-function)">.injectEndpoints</span><span style="color: var(--shiki-color-text)">({</span></span>
<span><span style="color: var(--shiki-color-text)">  </span><span style="color: var(--shiki-token-function)">endpoints</span><span style="color: var(--shiki-token-keyword)">:</span><span style="color: var(--shiki-color-text)"> (builder) </span><span style="color: var(--shiki-token-keyword)">=&gt;</span><span style="color: var(--shiki-color-text)"> ({</span></span>
<span><span style="color: var(--shiki-color-text)">    getPosts</span><span style="color: var(--shiki-token-keyword)">:</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-constant)">builder</span><span style="color: var(--shiki-token-function)">.query</span><span style="color: var(--shiki-color-text)">({</span></span>
<span><span style="color: var(--shiki-color-text)">      </span><span style="color: var(--shiki-token-function)">query</span><span style="color: var(--shiki-token-keyword)">:</span><span style="color: var(--shiki-color-text)"> () </span><span style="color: var(--shiki-token-keyword)">=&gt;</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string-expression)">&#39;/posts&#39;</span><span style="color: var(--shiki-token-punctuation)">,</span></span>
<span><span style="color: var(--shiki-color-text)">      </span><span style="color: var(--shiki-token-function)">transformResponse</span><span style="color: var(--shiki-token-keyword)">:</span><span style="color: var(--shiki-color-text)"> (responseData) </span><span style="color: var(--shiki-token-keyword)">=&gt;</span><span style="color: var(--shiki-color-text)"> {</span></span>
<span><span style="color: var(--shiki-color-text)">        </span><span style="color: var(--shiki-token-keyword)">let</span><span style="color: var(--shiki-color-text)"> min </span><span style="color: var(--shiki-token-keyword)">=</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-constant)">1</span></span>
<span><span style="color: var(--shiki-color-text)">        </span><span style="color: var(--shiki-token-keyword)">const</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-constant)">loadedPosts</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">=</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-constant)">responseData</span><span style="color: var(--shiki-token-function)">.map</span><span style="color: var(--shiki-color-text)">((post) </span><span style="color: var(--shiki-token-keyword)">=&gt;</span><span style="color: var(--shiki-color-text)"> {</span></span>
<span><span style="color: var(--shiki-color-text)">          </span><span style="color: var(--shiki-token-keyword)">if</span><span style="color: var(--shiki-color-text)"> (</span><span style="color: var(--shiki-token-keyword)">!</span><span style="color: var(--shiki-token-constant)">post</span><span style="color: var(--shiki-color-text)">?.date)</span></span>
<span><span style="color: var(--shiki-color-text)">            </span><span style="color: var(--shiki-token-constant)">post</span><span style="color: var(--shiki-color-text)">.date </span><span style="color: var(--shiki-token-keyword)">=</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-function)">sub</span><span style="color: var(--shiki-color-text)">(</span><span style="color: var(--shiki-token-keyword)">new</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-function)">Date</span><span style="color: var(--shiki-color-text)">()</span><span style="color: var(--shiki-token-punctuation)">,</span><span style="color: var(--shiki-color-text)"> { minutes</span><span style="color: var(--shiki-token-keyword)">:</span><span style="color: var(--shiki-color-text)"> min</span><span style="color: var(--shiki-token-keyword)">++</span><span style="color: var(--shiki-color-text)"> })</span><span style="color: var(--shiki-token-function)">.toISOString</span><span style="color: var(--shiki-color-text)">()</span></span>
<span><span style="color: var(--shiki-color-text)">          </span><span style="color: var(--shiki-token-keyword)">if</span><span style="color: var(--shiki-color-text)"> (</span><span style="color: var(--shiki-token-keyword)">!</span><span style="color: var(--shiki-token-constant)">post</span><span style="color: var(--shiki-color-text)">?.reactions)</span></span>
<span><span style="color: var(--shiki-color-text)">            </span><span style="color: var(--shiki-token-constant)">post</span><span style="color: var(--shiki-color-text)">.reactions </span><span style="color: var(--shiki-token-keyword)">=</span><span style="color: var(--shiki-color-text)"> {</span></span>
<span><span style="color: var(--shiki-color-text)">              thumbsUp</span><span style="color: var(--shiki-token-keyword)">:</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-constant)">0</span><span style="color: var(--shiki-token-punctuation)">,</span></span>
<span><span style="color: var(--shiki-color-text)">              wow</span><span style="color: var(--shiki-token-keyword)">:</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-constant)">0</span><span style="color: var(--shiki-token-punctuation)">,</span></span>
<span><span style="color: var(--shiki-color-text)">              heart</span><span style="color: var(--shiki-token-keyword)">:</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-constant)">0</span><span style="color: var(--shiki-token-punctuation)">,</span></span>
<span><span style="color: var(--shiki-color-text)">              rocket</span><span style="color: var(--shiki-token-keyword)">:</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-constant)">0</span><span style="color: var(--shiki-token-punctuation)">,</span></span>
<span><span style="color: var(--shiki-color-text)">              coffee</span><span style="color: var(--shiki-token-keyword)">:</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-constant)">0</span><span style="color: var(--shiki-token-punctuation)">,</span></span>
<span><span style="color: var(--shiki-color-text)">            }</span></span>
<span><span style="color: var(--shiki-color-text)">          </span><span style="color: var(--shiki-token-keyword)">return</span><span style="color: var(--shiki-color-text)"> post</span></span>
<span><span style="color: var(--shiki-color-text)">        })</span></span>
<span><span style="color: var(--shiki-color-text)">        </span><span style="color: var(--shiki-token-keyword)">return</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-constant)">postsAdapter</span><span style="color: var(--shiki-token-function)">.setAll</span><span style="color: var(--shiki-color-text)">(initialState</span><span style="color: var(--shiki-token-punctuation)">,</span><span style="color: var(--shiki-color-text)"> loadedPosts)</span></span>
<span><span style="color: var(--shiki-color-text)">      }</span><span style="color: var(--shiki-token-punctuation)">,</span></span>
<span><span style="color: var(--shiki-color-text)">      </span><span style="color: var(--shiki-token-function)">providesTags</span><span style="color: var(--shiki-token-keyword)">:</span><span style="color: var(--shiki-color-text)"> (result</span><span style="color: var(--shiki-token-punctuation)">,</span><span style="color: var(--shiki-color-text)"> error</span><span style="color: var(--shiki-token-punctuation)">,</span><span style="color: var(--shiki-color-text)"> arg) </span><span style="color: var(--shiki-token-keyword)">=&gt;</span><span style="color: var(--shiki-color-text)"> [</span></span>
<span><span style="color: var(--shiki-color-text)">        { type</span><span style="color: var(--shiki-token-keyword)">:</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string-expression)">&#39;Post&#39;</span><span style="color: var(--shiki-token-punctuation)">,</span><span style="color: var(--shiki-color-text)"> id</span><span style="color: var(--shiki-token-keyword)">:</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string-expression)">&#39;LIST&#39;</span><span style="color: var(--shiki-color-text)"> }</span><span style="color: var(--shiki-token-punctuation)">,</span></span>
<span><span style="color: var(--shiki-color-text)">        </span><span style="color: var(--shiki-token-keyword)">...</span><span style="color: var(--shiki-token-constant)">result</span><span style="color: var(--shiki-token-function)">.</span><span style="color: var(--shiki-token-constant)">ids</span><span style="color: var(--shiki-token-function)">.map</span><span style="color: var(--shiki-color-text)">((id) </span><span style="color: var(--shiki-token-keyword)">=&gt;</span><span style="color: var(--shiki-color-text)"> ({ type</span><span style="color: var(--shiki-token-keyword)">:</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string-expression)">&#39;Post&#39;</span><span style="color: var(--shiki-token-punctuation)">,</span><span style="color: var(--shiki-color-text)"> id }))</span><span style="color: var(--shiki-token-punctuation)">,</span></span>
<span><span style="color: var(--shiki-color-text)">      ]</span><span style="color: var(--shiki-token-punctuation)">,</span></span>
<span><span style="color: var(--shiki-color-text)">    })</span><span style="color: var(--shiki-token-punctuation)">,</span></span>
<span><span style="color: var(--shiki-color-text)">    </span><span style="color: var(--shiki-token-comment)">// ↘️↘️↘️↘️↘️↘️↘️↘️↘️↘️↘️↘️↘️↘️↘️↘️↘️↘️↘️↘️↘️↘️we&#39;ve added this method ↘↘️↘️↘️↘️↘️↘️↘️↘️↘️↘️↘️</span></span>
<span><span style="color: var(--shiki-color-text)">    getPostsByUserId</span><span style="color: var(--shiki-token-keyword)">:</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-constant)">builder</span><span style="color: var(--shiki-token-function)">.query</span><span style="color: var(--shiki-color-text)">({</span></span>
<span><span style="color: var(--shiki-color-text)">      </span><span style="color: var(--shiki-token-function)">query</span><span style="color: var(--shiki-token-keyword)">:</span><span style="color: var(--shiki-color-text)"> (id) </span><span style="color: var(--shiki-token-keyword)">=&gt;</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string-expression)">`/posts/?userId=</span><span style="color: var(--shiki-token-keyword)">${</span><span style="color: var(--shiki-color-text)">id</span><span style="color: var(--shiki-token-keyword)">}</span><span style="color: var(--shiki-token-string-expression)">`</span><span style="color: var(--shiki-token-punctuation)">,</span></span>
<span><span style="color: var(--shiki-color-text)">      </span><span style="color: var(--shiki-token-function)">transformResponse</span><span style="color: var(--shiki-token-keyword)">:</span><span style="color: var(--shiki-color-text)"> (responseData) </span><span style="color: var(--shiki-token-keyword)">=&gt;</span><span style="color: var(--shiki-color-text)"> {</span></span>
<span><span style="color: var(--shiki-color-text)">        </span><span style="color: var(--shiki-token-keyword)">let</span><span style="color: var(--shiki-color-text)"> min </span><span style="color: var(--shiki-token-keyword)">=</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-constant)">1</span></span>
<span><span style="color: var(--shiki-color-text)">        </span><span style="color: var(--shiki-token-keyword)">const</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-constant)">loadedPosts</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">=</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-constant)">responseData</span><span style="color: var(--shiki-token-function)">.map</span><span style="color: var(--shiki-color-text)">((post) </span><span style="color: var(--shiki-token-keyword)">=&gt;</span><span style="color: var(--shiki-color-text)"> {</span></span>
<span><span style="color: var(--shiki-color-text)">          </span><span style="color: var(--shiki-token-keyword)">if</span><span style="color: var(--shiki-color-text)"> (</span><span style="color: var(--shiki-token-keyword)">!</span><span style="color: var(--shiki-token-constant)">post</span><span style="color: var(--shiki-color-text)">?.date)</span></span>
<span><span style="color: var(--shiki-color-text)">            </span><span style="color: var(--shiki-token-constant)">post</span><span style="color: var(--shiki-color-text)">.date </span><span style="color: var(--shiki-token-keyword)">=</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-function)">sub</span><span style="color: var(--shiki-color-text)">(</span><span style="color: var(--shiki-token-keyword)">new</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-function)">Date</span><span style="color: var(--shiki-color-text)">()</span><span style="color: var(--shiki-token-punctuation)">,</span><span style="color: var(--shiki-color-text)"> { minutes</span><span style="color: var(--shiki-token-keyword)">:</span><span style="color: var(--shiki-color-text)"> min</span><span style="color: var(--shiki-token-keyword)">++</span><span style="color: var(--shiki-color-text)"> })</span><span style="color: var(--shiki-token-function)">.toISOString</span><span style="color: var(--shiki-color-text)">()</span></span>
<span><span style="color: var(--shiki-color-text)">          </span><span style="color: var(--shiki-token-keyword)">if</span><span style="color: var(--shiki-color-text)"> (</span><span style="color: var(--shiki-token-keyword)">!</span><span style="color: var(--shiki-token-constant)">post</span><span style="color: var(--shiki-color-text)">?.reactions)</span></span>
<span><span style="color: var(--shiki-color-text)">            </span><span style="color: var(--shiki-token-constant)">post</span><span style="color: var(--shiki-color-text)">.reactions </span><span style="color: var(--shiki-token-keyword)">=</span><span style="color: var(--shiki-color-text)"> {</span></span>
<span><span style="color: var(--shiki-color-text)">              thumbsUp</span><span style="color: var(--shiki-token-keyword)">:</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-constant)">0</span><span style="color: var(--shiki-token-punctuation)">,</span></span>
<span><span style="color: var(--shiki-color-text)">              wow</span><span style="color: var(--shiki-token-keyword)">:</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-constant)">0</span><span style="color: var(--shiki-token-punctuation)">,</span></span>
<span><span style="color: var(--shiki-color-text)">              heart</span><span style="color: var(--shiki-token-keyword)">:</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-constant)">0</span><span style="color: var(--shiki-token-punctuation)">,</span></span>
<span><span style="color: var(--shiki-color-text)">              rocket</span><span style="color: var(--shiki-token-keyword)">:</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-constant)">0</span><span style="color: var(--shiki-token-punctuation)">,</span></span>
<span><span style="color: var(--shiki-color-text)">              coffee</span><span style="color: var(--shiki-token-keyword)">:</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-constant)">0</span><span style="color: var(--shiki-token-punctuation)">,</span></span>
<span><span style="color: var(--shiki-color-text)">            }</span></span>
<span><span style="color: var(--shiki-color-text)">          </span><span style="color: var(--shiki-token-keyword)">return</span><span style="color: var(--shiki-color-text)"> post</span></span>
<span><span style="color: var(--shiki-color-text)">        })</span></span>
<span><span style="color: var(--shiki-color-text)">        </span><span style="color: var(--shiki-token-keyword)">return</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-constant)">postsAdapter</span><span style="color: var(--shiki-token-function)">.setAll</span><span style="color: var(--shiki-color-text)">(initialState</span><span style="color: var(--shiki-token-punctuation)">,</span><span style="color: var(--shiki-color-text)"> loadedPosts)</span></span>
<span><span style="color: var(--shiki-color-text)">      }</span><span style="color: var(--shiki-token-punctuation)">,</span></span>
<span><span style="color: var(--shiki-color-text)">      </span><span style="color: var(--shiki-token-function)">providesTags</span><span style="color: var(--shiki-token-keyword)">:</span><span style="color: var(--shiki-color-text)"> (result</span><span style="color: var(--shiki-token-punctuation)">,</span><span style="color: var(--shiki-color-text)"> error</span><span style="color: var(--shiki-token-punctuation)">,</span><span style="color: var(--shiki-color-text)"> arg) </span><span style="color: var(--shiki-token-keyword)">=&gt;</span><span style="color: var(--shiki-color-text)"> [</span></span>
<span><span style="color: var(--shiki-color-text)">        </span><span style="color: var(--shiki-token-keyword)">...</span><span style="color: var(--shiki-token-constant)">result</span><span style="color: var(--shiki-token-function)">.</span><span style="color: var(--shiki-token-constant)">ids</span><span style="color: var(--shiki-token-function)">.map</span><span style="color: var(--shiki-color-text)">((id) </span><span style="color: var(--shiki-token-keyword)">=&gt;</span><span style="color: var(--shiki-color-text)"> ({ type</span><span style="color: var(--shiki-token-keyword)">:</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string-expression)">&#39;Post&#39;</span><span style="color: var(--shiki-token-punctuation)">,</span><span style="color: var(--shiki-color-text)"> id }))</span><span style="color: var(--shiki-token-punctuation)">,</span></span>
<span><span style="color: var(--shiki-color-text)">      ]</span><span style="color: var(--shiki-token-punctuation)">,</span></span>
<span><span style="color: var(--shiki-color-text)">    })</span><span style="color: var(--shiki-token-punctuation)">,</span></span>
<span></span>
<span><span style="color: var(--shiki-color-text)">    </span><span style="color: var(--shiki-token-comment)">// addNewPost Method</span></span>
<span></span>
<span><span style="color: var(--shiki-color-text)">    </span><span style="color: var(--shiki-token-comment)">// upadatePost method</span></span>
<span></span>
<span><span style="color: var(--shiki-color-text)">    </span><span style="color: var(--shiki-token-comment)">// deletePost method</span></span>
<span><span style="color: var(--shiki-color-text)">  })</span><span style="color: var(--shiki-token-punctuation)">,</span></span>
<span><span style="color: var(--shiki-color-text)">})</span></span>
<span></span>

Let's export the created hook by RTQ query!

<span><span style="color: var(--shiki-token-keyword)">export</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">const</span><span style="color: var(--shiki-color-text)"> { </span><span style="color: var(--shiki-token-constant)">useGetPostsQuery</span><span style="color: var(--shiki-token-punctuation)">,</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-constant)">useGetPostsByUserIdQuery</span><span style="color: var(--shiki-color-text)">  } </span><span style="color: var(--shiki-token-keyword)">=</span><span style="color: var(--shiki-color-text)"> extendedApiSlice</span></span>
<span></span>

Implement the addNewPost endpoint method

For addNewPost, we are doing something a bit different from the other methods. We are using builder.mutation instead of builder.query because we are changing the state of our data.

We are passing in an initialPost object, which will contain the post we intend to add to the state. For the URL, we are passing in /posts, which is the endpoint we are going to hit. We are using the method: 'POST' property to indicate that we are going to add a new post. We are using the body property to pass in the initialPost object. We are also using the invalidatesTags property to invalidate the cache of the posts list. We are using type: 'Post', id: "LIST" to invalidate the cache of the posts list.

<span></span>
<span><span style="color: var(--shiki-color-text)"> addNewPost</span><span style="color: var(--shiki-token-punctuation)">:</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-constant)">builder</span><span style="color: var(--shiki-token-function)">.mutation</span><span style="color: var(--shiki-color-text)">({</span></span>
<span><span style="color: var(--shiki-color-text)">            </span><span style="color: var(--shiki-token-function)">query</span><span style="color: var(--shiki-token-keyword)">:</span><span style="color: var(--shiki-color-text)"> initialPost </span><span style="color: var(--shiki-token-keyword)">=&gt;</span><span style="color: var(--shiki-color-text)"> ({</span></span>
<span><span style="color: var(--shiki-color-text)">                url</span><span style="color: var(--shiki-token-keyword)">:</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string-expression)">&#39;/posts&#39;</span><span style="color: var(--shiki-token-punctuation)">,</span></span>
<span><span style="color: var(--shiki-color-text)">                method</span><span style="color: var(--shiki-token-keyword)">:</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string-expression)">&#39;POST&#39;</span><span style="color: var(--shiki-token-punctuation)">,</span></span>
<span><span style="color: var(--shiki-color-text)">                body</span><span style="color: var(--shiki-token-keyword)">:</span><span style="color: var(--shiki-color-text)"> {</span></span>
<span><span style="color: var(--shiki-color-text)">                    </span><span style="color: var(--shiki-token-keyword)">...</span><span style="color: var(--shiki-color-text)">initialPost</span><span style="color: var(--shiki-token-punctuation)">,</span></span>
<span><span style="color: var(--shiki-color-text)">                    userId</span><span style="color: var(--shiki-token-keyword)">:</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-function)">Number</span><span style="color: var(--shiki-color-text)">(</span><span style="color: var(--shiki-token-constant)">initialPost</span><span style="color: var(--shiki-color-text)">.userId)</span><span style="color: var(--shiki-token-punctuation)">,</span></span>
<span><span style="color: var(--shiki-color-text)">                    date</span><span style="color: var(--shiki-token-keyword)">:</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">new</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-function)">Date</span><span style="color: var(--shiki-color-text)">()</span><span style="color: var(--shiki-token-function)">.toISOString</span><span style="color: var(--shiki-color-text)">()</span><span style="color: var(--shiki-token-punctuation)">,</span></span>
<span><span style="color: var(--shiki-color-text)">                    reactions</span><span style="color: var(--shiki-token-keyword)">:</span><span style="color: var(--shiki-color-text)"> {</span></span>
<span><span style="color: var(--shiki-color-text)">                        thumbsUp</span><span style="color: var(--shiki-token-keyword)">:</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-constant)">0</span><span style="color: var(--shiki-token-punctuation)">,</span></span>
<span><span style="color: var(--shiki-color-text)">                        wow</span><span style="color: var(--shiki-token-keyword)">:</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-constant)">0</span><span style="color: var(--shiki-token-punctuation)">,</span></span>
<span><span style="color: var(--shiki-color-text)">                        heart</span><span style="color: var(--shiki-token-keyword)">:</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-constant)">0</span><span style="color: var(--shiki-token-punctuation)">,</span></span>
<span><span style="color: var(--shiki-color-text)">                        rocket</span><span style="color: var(--shiki-token-keyword)">:</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-constant)">0</span><span style="color: var(--shiki-token-punctuation)">,</span></span>
<span><span style="color: var(--shiki-color-text)">                        coffee</span><span style="color: var(--shiki-token-keyword)">:</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-constant)">0</span></span>
<span><span style="color: var(--shiki-color-text)">                    }</span></span>
<span><span style="color: var(--shiki-color-text)">                }</span></span>
<span><span style="color: var(--shiki-color-text)">            })</span><span style="color: var(--shiki-token-punctuation)">,</span></span>
<span><span style="color: var(--shiki-color-text)">            invalidatesTags</span><span style="color: var(--shiki-token-keyword)">:</span><span style="color: var(--shiki-color-text)"> [</span></span>
<span><span style="color: var(--shiki-color-text)">                { type</span><span style="color: var(--shiki-token-keyword)">:</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string-expression)">&#39;Post&#39;</span><span style="color: var(--shiki-token-punctuation)">,</span><span style="color: var(--shiki-color-text)"> id</span><span style="color: var(--shiki-token-keyword)">:</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string-expression)">&quot;LIST&quot;</span><span style="color: var(--shiki-color-text)"> }</span></span>
<span><span style="color: var(--shiki-color-text)">            ]</span></span>
<span><span style="color: var(--shiki-color-text)">        })</span><span style="color: var(--shiki-token-punctuation)">,</span></span>
<span></span>
<span></span>

Implement the updatePost endpoint method

Let's handle updating a post. We will name the method updatePost, which will be similar to the method above. The method will take in an initialPost object, and for the URL, we are passing the initialPost ID to allow us to update that individual post. We are using the method: 'PUT' to indicate that we are going to update the post.

For the body, we are just spreading in the initialPost object, then updating the date property with the current date. Finally, for invalidatesTags, we are going to invalidate the cache of the post that was updated. That's why we are using the arg.id to invalidate the cache of that individual post.

<span><span style="color: var(--shiki-color-text)">  updatePost</span><span style="color: var(--shiki-token-punctuation)">:</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-constant)">builder</span><span style="color: var(--shiki-token-function)">.mutation</span><span style="color: var(--shiki-color-text)">({</span></span>
<span><span style="color: var(--shiki-color-text)">            </span><span style="color: var(--shiki-token-function)">query</span><span style="color: var(--shiki-token-keyword)">:</span><span style="color: var(--shiki-color-text)"> initialPost </span><span style="color: var(--shiki-token-keyword)">=&gt;</span><span style="color: var(--shiki-color-text)"> ({</span></span>
<span><span style="color: var(--shiki-color-text)">                url</span><span style="color: var(--shiki-token-keyword)">:</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string-expression)">`/posts/</span><span style="color: var(--shiki-token-keyword)">${</span><span style="color: var(--shiki-token-constant)">initialPost</span><span style="color: var(--shiki-color-text)">.id</span><span style="color: var(--shiki-token-keyword)">}</span><span style="color: var(--shiki-token-string-expression)">`</span><span style="color: var(--shiki-token-punctuation)">,</span></span>
<span><span style="color: var(--shiki-color-text)">                method</span><span style="color: var(--shiki-token-keyword)">:</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string-expression)">&#39;PUT&#39;</span><span style="color: var(--shiki-token-punctuation)">,</span></span>
<span><span style="color: var(--shiki-color-text)">                body</span><span style="color: var(--shiki-token-keyword)">:</span><span style="color: var(--shiki-color-text)"> {</span></span>
<span><span style="color: var(--shiki-color-text)">                    </span><span style="color: var(--shiki-token-keyword)">...</span><span style="color: var(--shiki-color-text)">initialPost</span><span style="color: var(--shiki-token-punctuation)">,</span></span>
<span><span style="color: var(--shiki-color-text)">                    date</span><span style="color: var(--shiki-token-keyword)">:</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">new</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-function)">Date</span><span style="color: var(--shiki-color-text)">()</span><span style="color: var(--shiki-token-function)">.toISOString</span><span style="color: var(--shiki-color-text)">()</span></span>
<span><span style="color: var(--shiki-color-text)">                }</span></span>
<span><span style="color: var(--shiki-color-text)">            })</span><span style="color: var(--shiki-token-punctuation)">,</span></span>
<span><span style="color: var(--shiki-color-text)">            </span><span style="color: var(--shiki-token-function)">invalidatesTags</span><span style="color: var(--shiki-token-keyword)">:</span><span style="color: var(--shiki-color-text)"> (result</span><span style="color: var(--shiki-token-punctuation)">,</span><span style="color: var(--shiki-color-text)"> error</span><span style="color: var(--shiki-token-punctuation)">,</span><span style="color: var(--shiki-color-text)"> arg) </span><span style="color: var(--shiki-token-keyword)">=&gt;</span><span style="color: var(--shiki-color-text)"> [</span></span>
<span><span style="color: var(--shiki-color-text)">                { type</span><span style="color: var(--shiki-token-keyword)">:</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string-expression)">&#39;Post&#39;</span><span style="color: var(--shiki-token-punctuation)">,</span><span style="color: var(--shiki-color-text)"> id</span><span style="color: var(--shiki-token-keyword)">:</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-constant)">arg</span><span style="color: var(--shiki-color-text)">.id }</span></span>
<span><span style="color: var(--shiki-color-text)">            ]</span></span>
<span><span style="color: var(--shiki-color-text)">        })</span><span style="color: var(--shiki-token-punctuation)">,</span></span>
<span></span>
<span></span>

Implement the deletePost endpoint method

Let's add the deletePost method! We are going to destructure the initialPost object and get the id property since we have no use for the other properties. Next, we are providing the id to the URL. The method will be DELETE in this case, and finally, we pass the id to the body. Finally, we are invalidating the cache of the post that was deleted by using the arg.id, which will remove the post from the cache.

<span><span style="color: var(--shiki-color-text)">   deletePost</span><span style="color: var(--shiki-token-punctuation)">:</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-constant)">builder</span><span style="color: var(--shiki-token-function)">.mutation</span><span style="color: var(--shiki-color-text)">({</span></span>
<span><span style="color: var(--shiki-color-text)">            </span><span style="color: var(--shiki-token-function)">query</span><span style="color: var(--shiki-token-keyword)">:</span><span style="color: var(--shiki-color-text)"> ({ id }) </span><span style="color: var(--shiki-token-keyword)">=&gt;</span><span style="color: var(--shiki-color-text)"> ({</span></span>
<span><span style="color: var(--shiki-color-text)">                url</span><span style="color: var(--shiki-token-keyword)">:</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string-expression)">`/posts/</span><span style="color: var(--shiki-token-keyword)">${</span><span style="color: var(--shiki-color-text)">id</span><span style="color: var(--shiki-token-keyword)">}</span><span style="color: var(--shiki-token-string-expression)">`</span><span style="color: var(--shiki-token-punctuation)">,</span></span>
<span><span style="color: var(--shiki-color-text)">                method</span><span style="color: var(--shiki-token-keyword)">:</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string-expression)">&#39;DELETE&#39;</span><span style="color: var(--shiki-token-punctuation)">,</span></span>
<span><span style="color: var(--shiki-color-text)">                body</span><span style="color: var(--shiki-token-keyword)">:</span><span style="color: var(--shiki-color-text)"> { id }</span></span>
<span><span style="color: var(--shiki-color-text)">            })</span><span style="color: var(--shiki-token-punctuation)">,</span></span>
<span><span style="color: var(--shiki-color-text)">            </span><span style="color: var(--shiki-token-function)">invalidatesTags</span><span style="color: var(--shiki-token-keyword)">:</span><span style="color: var(--shiki-color-text)"> (result</span><span style="color: var(--shiki-token-punctuation)">,</span><span style="color: var(--shiki-color-text)"> error</span><span style="color: var(--shiki-token-punctuation)">,</span><span style="color: var(--shiki-color-text)"> arg) </span><span style="color: var(--shiki-token-keyword)">=&gt;</span><span style="color: var(--shiki-color-text)"> [</span></span>
<span><span style="color: var(--shiki-color-text)">                { type</span><span style="color: var(--shiki-token-keyword)">:</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string-expression)">&#39;Post&#39;</span><span style="color: var(--shiki-token-punctuation)">,</span><span style="color: var(--shiki-color-text)"> id</span><span style="color: var(--shiki-token-keyword)">:</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-constant)">arg</span><span style="color: var(--shiki-color-text)">.id }</span></span>
<span><span style="color: var(--shiki-color-text)">            ]</span></span>
<span><span style="color: var(--shiki-color-text)">        })</span><span style="color: var(--shiki-token-punctuation)">,</span></span>
<span></span>

Export the auto-generated hooks from the postsApi slice

We have made some good progress, let's now export the custom hooks created by RTQ query!

<span><span style="color: var(--shiki-token-keyword)">export</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">const</span><span style="color: var(--shiki-color-text)"> {</span></span>
<span><span style="color: var(--shiki-color-text)">  </span><span style="color: var(--shiki-token-constant)">useGetPostsQuery</span><span style="color: var(--shiki-token-punctuation)">,</span></span>
<span><span style="color: var(--shiki-color-text)">  </span><span style="color: var(--shiki-token-constant)">useGetPostsByUserIdQuery</span><span style="color: var(--shiki-token-punctuation)">,</span></span>
<span></span>
<span><span style="color: var(--shiki-color-text)">  </span><span style="color: var(--shiki-token-comment)">/**</span></span>
<span><span style="color: var(--shiki-token-comment)">   * For mutation we get ****Mutation</span></span>
<span><span style="color: var(--shiki-token-comment)">   * For queries we get ****Query</span></span>
<span><span style="color: var(--shiki-token-comment)">   * ⬇️ ⬇️ ⬇️ ⬇️ ⬇️ ⬇️</span></span>
<span><span style="color: var(--shiki-token-comment)">   */</span></span>
<span><span style="color: var(--shiki-color-text)">  </span><span style="color: var(--shiki-token-constant)">useAddNewPostMutation</span><span style="color: var(--shiki-token-punctuation)">,</span></span>
<span><span style="color: var(--shiki-color-text)">  </span><span style="color: var(--shiki-token-constant)">useUpdatePostMutation</span><span style="color: var(--shiki-token-punctuation)">,</span></span>
<span><span style="color: var(--shiki-color-text)">  </span><span style="color: var(--shiki-token-constant)">useDeletePostMutation</span><span style="color: var(--shiki-token-punctuation)">,</span></span>
<span><span style="color: var(--shiki-color-text)">} </span><span style="color: var(--shiki-token-keyword)">=</span><span style="color: var(--shiki-color-text)"> extendedApiSlice</span></span>
<span></span>
<span><span style="color: var(--shiki-token-comment)">///</span></span>
<span></span>

Implement the AddPostForm component

Let's head to the AddPostForm. Once there, we'll import the useAddNewPostMutation from the postSlice to perform spell check.

<span><span style="color: var(--shiki-token-keyword)">import</span><span style="color: var(--shiki-color-text)"> { useAddNewPostMutation } </span><span style="color: var(--shiki-token-keyword)">from</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string-expression)">&#39;./postsSlice&#39;</span></span>
<span></span>

We will proceed with implementing the useAddNewPostMutation function. This function will return two values, the addNewPost function which is used to add a new post and an object that contains the state of the function. The state object will include properties such as isLoading, isError, Error, etc.

<span><span style="color: var(--shiki-token-keyword)">const</span><span style="color: var(--shiki-color-text)"> [</span><span style="color: var(--shiki-token-constant)">addNewPost</span><span style="color: var(--shiki-token-punctuation)">,</span><span style="color: var(--shiki-color-text)"> { </span><span style="color: var(--shiki-token-constant)">isLoading</span><span style="color: var(--shiki-color-text)"> }] </span><span style="color: var(--shiki-token-keyword)">=</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-function)">useAddNewPostMutation</span><span style="color: var(--shiki-color-text)">()</span></span>
<span></span>

To enable the user to save a post, we need to create a canSave value that checks if the form is not empty and not currently loading. Only if both conditions are met, can the user save the post.

<span><span style="color: var(--shiki-token-keyword)">const</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-constant)">canSave</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">=</span><span style="color: var(--shiki-color-text)"> [title</span><span style="color: var(--shiki-token-punctuation)">,</span><span style="color: var(--shiki-color-text)"> content</span><span style="color: var(--shiki-token-punctuation)">,</span><span style="color: var(--shiki-color-text)"> userId]</span><span style="color: var(--shiki-token-function)">.every</span><span style="color: var(--shiki-color-text)">(Boolean) </span><span style="color: var(--shiki-token-keyword)">&amp;&amp;</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">!</span><span style="color: var(--shiki-color-text)">isLoading</span></span>
<span></span>

The onSavePostClicked function needs to be updated by calling addNewPost function with the required parameters to create a post, which will be obtained from the form. We will use the await keyword to ensure that the function is completed before moving to the next step. We will also continue to use unwrap since we are still in a try-catch block.

<span><span style="color: var(--shiki-token-keyword)">const</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-function)">onSavePostClicked</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">=</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">async</span><span style="color: var(--shiki-color-text)"> () </span><span style="color: var(--shiki-token-keyword)">=&gt;</span><span style="color: var(--shiki-color-text)"> {</span></span>
<span><span style="color: var(--shiki-color-text)">  </span><span style="color: var(--shiki-token-keyword)">if</span><span style="color: var(--shiki-color-text)"> (canSave) {</span></span>
<span><span style="color: var(--shiki-color-text)">    </span><span style="color: var(--shiki-token-keyword)">try</span><span style="color: var(--shiki-color-text)"> {</span></span>
<span><span style="color: var(--shiki-color-text)">      </span><span style="color: var(--shiki-token-keyword)">await</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-function)">addNewPost</span><span style="color: var(--shiki-color-text)">({ title</span><span style="color: var(--shiki-token-punctuation)">,</span><span style="color: var(--shiki-color-text)"> body</span><span style="color: var(--shiki-token-keyword)">:</span><span style="color: var(--shiki-color-text)"> content</span><span style="color: var(--shiki-token-punctuation)">,</span><span style="color: var(--shiki-color-text)"> userId })</span><span style="color: var(--shiki-token-function)">.unwrap</span><span style="color: var(--shiki-color-text)">()</span></span>
<span></span>
<span><span style="color: var(--shiki-color-text)">      </span><span style="color: var(--shiki-token-function)">setTitle</span><span style="color: var(--shiki-color-text)">(</span><span style="color: var(--shiki-token-string-expression)">&#39;&#39;</span><span style="color: var(--shiki-color-text)">)</span></span>
<span><span style="color: var(--shiki-color-text)">      </span><span style="color: var(--shiki-token-function)">setContent</span><span style="color: var(--shiki-color-text)">(</span><span style="color: var(--shiki-token-string-expression)">&#39;&#39;</span><span style="color: var(--shiki-color-text)">)</span></span>
<span><span style="color: var(--shiki-color-text)">      </span><span style="color: var(--shiki-token-function)">setUserId</span><span style="color: var(--shiki-color-text)">(</span><span style="color: var(--shiki-token-string-expression)">&#39;&#39;</span><span style="color: var(--shiki-color-text)">)</span></span>
<span><span style="color: var(--shiki-color-text)">      </span><span style="color: var(--shiki-token-function)">navigate</span><span style="color: var(--shiki-color-text)">(</span><span style="color: var(--shiki-token-string-expression)">&#39;/&#39;</span><span style="color: var(--shiki-color-text)">)</span></span>
<span><span style="color: var(--shiki-color-text)">    } </span><span style="color: var(--shiki-token-keyword)">catch</span><span style="color: var(--shiki-color-text)"> (err) {</span></span>
<span><span style="color: var(--shiki-color-text)">      </span><span style="color: var(--shiki-token-constant)">console</span><span style="color: var(--shiki-token-function)">.error</span><span style="color: var(--shiki-color-text)">(</span><span style="color: var(--shiki-token-string-expression)">&#39;Failed to save the post&#39;</span><span style="color: var(--shiki-token-punctuation)">,</span><span style="color: var(--shiki-color-text)"> err)</span></span>
<span><span style="color: var(--shiki-color-text)">    }</span></span>
<span><span style="color: var(--shiki-color-text)">  }</span></span>
<span><span style="color: var(--shiki-color-text)">}</span></span>
<span></span>

Implement the EditPostForm component

Let's proceed to the EditPostForm component which will handle updating and deleting posts. To begin, we need to import the mutation from postSlice module.

<span><span style="color: var(--shiki-token-keyword)">import</span><span style="color: var(--shiki-color-text)"> { useUpdatePostMutation</span><span style="color: var(--shiki-token-punctuation)">,</span><span style="color: var(--shiki-color-text)"> useDeletePostMutation } </span><span style="color: var(--shiki-token-keyword)">from</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string-expression)">&#39;./postsSlice&#39;</span></span>
<span></span>

Once again inside the component let's paste the mutation functions. We will use the useUpdatePostMutation to update the post and useDeletePostMutation to delete the post.

<span><span style="color: var(--shiki-token-keyword)">const</span><span style="color: var(--shiki-color-text)"> [</span><span style="color: var(--shiki-token-constant)">updatePost</span><span style="color: var(--shiki-token-punctuation)">,</span><span style="color: var(--shiki-color-text)"> { </span><span style="color: var(--shiki-token-constant)">isLoading</span><span style="color: var(--shiki-color-text)"> }] </span><span style="color: var(--shiki-token-keyword)">=</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-function)">useUpdatePostMutation</span><span style="color: var(--shiki-color-text)">()</span></span>
<span><span style="color: var(--shiki-token-keyword)">const</span><span style="color: var(--shiki-color-text)"> [</span><span style="color: var(--shiki-token-constant)">deletePost</span><span style="color: var(--shiki-color-text)">] </span><span style="color: var(--shiki-token-keyword)">=</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-function)">useDeletePostMutation</span><span style="color: var(--shiki-color-text)">()</span></span>
<span></span>

We need to create a canSave value in the EditPostForm component to check if the user can update the post. The canSave value will be similar to the one used in AddPostForm. It will check if there is a post and if loading is false, then only the user can update the post.

<span><span style="color: var(--shiki-token-keyword)">const</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-constant)">canSave</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">=</span><span style="color: var(--shiki-color-text)"> [title</span><span style="color: var(--shiki-token-punctuation)">,</span><span style="color: var(--shiki-color-text)"> content</span><span style="color: var(--shiki-token-punctuation)">,</span><span style="color: var(--shiki-color-text)"> userId]</span><span style="color: var(--shiki-token-function)">.every</span><span style="color: var(--shiki-color-text)">(Boolean) </span><span style="color: var(--shiki-token-keyword)">&amp;&amp;</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">!</span><span style="color: var(--shiki-color-text)">isLoading</span></span>
<span></span>

Now let's update the onSavePostClicked function which will handle updating the post. We just need to call the updatePost function provided by the useUpdatePostMutation() hook and await its result. We will pass in the post ID and the updated post object. That's it, we're done!

<span><span style="color: var(--shiki-token-keyword)">const</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-function)">onSavePostClicked</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">=</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">async</span><span style="color: var(--shiki-color-text)"> () </span><span style="color: var(--shiki-token-keyword)">=&gt;</span><span style="color: var(--shiki-color-text)"> {</span></span>
<span><span style="color: var(--shiki-color-text)">  </span><span style="color: var(--shiki-token-keyword)">if</span><span style="color: var(--shiki-color-text)"> (canSave) {</span></span>
<span><span style="color: var(--shiki-color-text)">    </span><span style="color: var(--shiki-token-keyword)">try</span><span style="color: var(--shiki-color-text)"> {</span></span>
<span><span style="color: var(--shiki-color-text)">      </span><span style="color: var(--shiki-token-keyword)">await</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-function)">updatePost</span><span style="color: var(--shiki-color-text)">({ id</span><span style="color: var(--shiki-token-keyword)">:</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-constant)">post</span><span style="color: var(--shiki-color-text)">.id</span><span style="color: var(--shiki-token-punctuation)">,</span><span style="color: var(--shiki-color-text)"> title</span><span style="color: var(--shiki-token-punctuation)">,</span><span style="color: var(--shiki-color-text)"> body</span><span style="color: var(--shiki-token-keyword)">:</span><span style="color: var(--shiki-color-text)"> content</span><span style="color: var(--shiki-token-punctuation)">,</span><span style="color: var(--shiki-color-text)"> userId })</span><span style="color: var(--shiki-token-function)">.unwrap</span><span style="color: var(--shiki-color-text)">()</span></span>
<span></span>
<span><span style="color: var(--shiki-color-text)">      </span><span style="color: var(--shiki-token-function)">setTitle</span><span style="color: var(--shiki-color-text)">(</span><span style="color: var(--shiki-token-string-expression)">&#39;&#39;</span><span style="color: var(--shiki-color-text)">)</span></span>
<span><span style="color: var(--shiki-color-text)">      </span><span style="color: var(--shiki-token-function)">setContent</span><span style="color: var(--shiki-color-text)">(</span><span style="color: var(--shiki-token-string-expression)">&#39;&#39;</span><span style="color: var(--shiki-color-text)">)</span></span>
<span><span style="color: var(--shiki-color-text)">      </span><span style="color: var(--shiki-token-function)">setUserId</span><span style="color: var(--shiki-color-text)">(</span><span style="color: var(--shiki-token-string-expression)">&#39;&#39;</span><span style="color: var(--shiki-color-text)">)</span></span>
<span><span style="color: var(--shiki-color-text)">      </span><span style="color: var(--shiki-token-function)">navigate</span><span style="color: var(--shiki-color-text)">(</span><span style="color: var(--shiki-token-string-expression)">`/post/</span><span style="color: var(--shiki-token-keyword)">${</span><span style="color: var(--shiki-color-text)">postId</span><span style="color: var(--shiki-token-keyword)">}</span><span style="color: var(--shiki-token-string-expression)">`</span><span style="color: var(--shiki-color-text)">)</span></span>
<span><span style="color: var(--shiki-color-text)">    } </span><span style="color: var(--shiki-token-keyword)">catch</span><span style="color: var(--shiki-color-text)"> (err) {</span></span>
<span><span style="color: var(--shiki-color-text)">      </span><span style="color: var(--shiki-token-constant)">console</span><span style="color: var(--shiki-token-function)">.error</span><span style="color: var(--shiki-color-text)">(</span><span style="color: var(--shiki-token-string-expression)">&#39;Failed to save the post&#39;</span><span style="color: var(--shiki-token-punctuation)">,</span><span style="color: var(--shiki-color-text)"> err)</span></span>
<span><span style="color: var(--shiki-color-text)">    }</span></span>
<span><span style="color: var(--shiki-color-text)">  }</span></span>
<span><span style="color: var(--shiki-color-text)">}</span></span>
<span></span>

To handle deleting a post, we need to create a similar function that will call the deletePost function provided by the useDeletePostMutation() hook. We will pass in the post ID to delete the post.

<span><span style="color: var(--shiki-token-keyword)">const</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-function)">onDeletePostClicked</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">=</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">async</span><span style="color: var(--shiki-color-text)"> () </span><span style="color: var(--shiki-token-keyword)">=&gt;</span><span style="color: var(--shiki-color-text)"> {</span></span>
<span><span style="color: var(--shiki-color-text)">  </span><span style="color: var(--shiki-token-keyword)">try</span><span style="color: var(--shiki-color-text)"> {</span></span>
<span><span style="color: var(--shiki-color-text)">    </span><span style="color: var(--shiki-token-keyword)">await</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-function)">deletePost</span><span style="color: var(--shiki-color-text)">({ id</span><span style="color: var(--shiki-token-keyword)">:</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-constant)">post</span><span style="color: var(--shiki-color-text)">.id })</span><span style="color: var(--shiki-token-function)">.unwrap</span><span style="color: var(--shiki-color-text)">()</span></span>
<span></span>
<span><span style="color: var(--shiki-color-text)">    </span><span style="color: var(--shiki-token-function)">setTitle</span><span style="color: var(--shiki-color-text)">(</span><span style="color: var(--shiki-token-string-expression)">&#39;&#39;</span><span style="color: var(--shiki-color-text)">)</span></span>
<span><span style="color: var(--shiki-color-text)">    </span><span style="color: var(--shiki-token-function)">setContent</span><span style="color: var(--shiki-color-text)">(</span><span style="color: var(--shiki-token-string-expression)">&#39;&#39;</span><span style="color: var(--shiki-color-text)">)</span></span>
<span><span style="color: var(--shiki-color-text)">    </span><span style="color: var(--shiki-token-function)">setUserId</span><span style="color: var(--shiki-color-text)">(</span><span style="color: var(--shiki-token-string-expression)">&#39;&#39;</span><span style="color: var(--shiki-color-text)">)</span></span>
<span><span style="color: var(--shiki-color-text)">    </span><span style="color: var(--shiki-token-function)">navigate</span><span style="color: var(--shiki-color-text)">(</span><span style="color: var(--shiki-token-string-expression)">&#39;/&#39;</span><span style="color: var(--shiki-color-text)">)</span></span>
<span><span style="color: var(--shiki-color-text)">  } </span><span style="color: var(--shiki-token-keyword)">catch</span><span style="color: var(--shiki-color-text)"> (err) {</span></span>
<span><span style="color: var(--shiki-color-text)">    </span><span style="color: var(--shiki-token-constant)">console</span><span style="color: var(--shiki-token-function)">.error</span><span style="color: var(--shiki-color-text)">(</span><span style="color: var(--shiki-token-string-expression)">&#39;Failed to delete the post&#39;</span><span style="color: var(--shiki-token-punctuation)">,</span><span style="color: var(--shiki-color-text)"> err)</span></span>
<span><span style="color: var(--shiki-color-text)">  }</span></span>
<span><span style="color: var(--shiki-color-text)">}</span></span>
<span></span>

Implement the UserPage component

Let's head over to the UserPage component, which will display the list of posts by a user. To start, we need to import the useGetPostsByUserIdQuery hook from the postSlice module.

<span><span style="color: var(--shiki-token-keyword)">import</span><span style="color: var(--shiki-color-text)"> { useGetPostsByUserIdQuery } </span><span style="color: var(--shiki-token-keyword)">from</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string-expression)">&#39;../posts/postsSlice&#39;</span></span>
<span></span>

In the UserPage component, we will use the useGetPostsByUserIdQuery hook to fetch the posts by user ID. We will pass in the user ID as the parameter to this hook. The hook will return the data, isLoading, isError, and error values which we will use to display the posts. We will define the data as postsForUser, and we will still have the same status of isLoading, isError, and isSuccess, which will help us render the posts conditionally.

<span><span style="color: var(--shiki-token-keyword)">const</span><span style="color: var(--shiki-color-text)"> {</span></span>
<span><span style="color: var(--shiki-color-text)">  data: </span><span style="color: var(--shiki-token-constant)">postsForUser</span><span style="color: var(--shiki-token-punctuation)">,</span></span>
<span><span style="color: var(--shiki-color-text)">  </span><span style="color: var(--shiki-token-constant)">isLoading</span><span style="color: var(--shiki-token-punctuation)">,</span></span>
<span><span style="color: var(--shiki-color-text)">  </span><span style="color: var(--shiki-token-constant)">isSuccess</span><span style="color: var(--shiki-token-punctuation)">,</span></span>
<span><span style="color: var(--shiki-color-text)">  </span><span style="color: var(--shiki-token-constant)">isError</span><span style="color: var(--shiki-token-punctuation)">,</span></span>
<span><span style="color: var(--shiki-color-text)">  </span><span style="color: var(--shiki-token-constant)">error</span><span style="color: var(--shiki-token-punctuation)">,</span></span>
<span><span style="color: var(--shiki-color-text)">} </span><span style="color: var(--shiki-token-keyword)">=</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-function)">useGetPostsByUserIdQuery</span><span style="color: var(--shiki-color-text)">(userId)</span></span>
<span></span>

Let's create the content for the UserPage component. We will define the content variable and check the isLoading, isError, and isSuccess statuses. If the status is isLoading, we will define the content as 'Loading...'. If the status is isError, we will define the content as the error value. If the status is isSuccess, we will destructure the postsForUser data into an object with ids and entities properties. Then, we will map through the ids and display the title of each post. We will use the entities object to look up the post data using the ID as the key.

<span><span style="color: var(--shiki-token-keyword)">let</span><span style="color: var(--shiki-color-text)"> content</span></span>
<span><span style="color: var(--shiki-token-keyword)">if</span><span style="color: var(--shiki-color-text)"> (isLoading) {</span></span>
<span><span style="color: var(--shiki-color-text)">  content </span><span style="color: var(--shiki-token-keyword)">=</span><span style="color: var(--shiki-color-text)"> &lt;</span><span style="color: var(--shiki-token-string-expression)">p</span><span style="color: var(--shiki-color-text)">&gt;Loading...&lt;/</span><span style="color: var(--shiki-token-string-expression)">p</span><span style="color: var(--shiki-color-text)">&gt;</span></span>
<span><span style="color: var(--shiki-color-text)">} </span><span style="color: var(--shiki-token-keyword)">else</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">if</span><span style="color: var(--shiki-color-text)"> (isSuccess) {</span></span>
<span><span style="color: var(--shiki-color-text)">  </span><span style="color: var(--shiki-token-keyword)">const</span><span style="color: var(--shiki-color-text)"> { </span><span style="color: var(--shiki-token-constant)">ids</span><span style="color: var(--shiki-token-punctuation)">,</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-constant)">entities</span><span style="color: var(--shiki-color-text)"> } </span><span style="color: var(--shiki-token-keyword)">=</span><span style="color: var(--shiki-color-text)"> postsForUser</span></span>
<span><span style="color: var(--shiki-color-text)">  content </span><span style="color: var(--shiki-token-keyword)">=</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-constant)">ids</span><span style="color: var(--shiki-token-function)">.map</span><span style="color: var(--shiki-color-text)">((id) </span><span style="color: var(--shiki-token-keyword)">=&gt;</span><span style="color: var(--shiki-color-text)"> (</span></span>
<span><span style="color: var(--shiki-color-text)">    &lt;</span><span style="color: var(--shiki-token-string-expression)">li</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-function)">key</span><span style="color: var(--shiki-token-keyword)">=</span><span style="color: var(--shiki-color-text)">{id}&gt;</span></span>
<span><span style="color: var(--shiki-color-text)">      &lt;</span><span style="color: var(--shiki-token-constant)">Link</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-function)">to</span><span style="color: var(--shiki-token-keyword)">=</span><span style="color: var(--shiki-color-text)">{</span><span style="color: var(--shiki-token-string-expression)">`/post/</span><span style="color: var(--shiki-token-keyword)">${</span><span style="color: var(--shiki-color-text)">id</span><span style="color: var(--shiki-token-keyword)">}</span><span style="color: var(--shiki-token-string-expression)">`</span><span style="color: var(--shiki-color-text)">}&gt;{entities[id].title}&lt;/</span><span style="color: var(--shiki-token-constant)">Link</span><span style="color: var(--shiki-color-text)">&gt;</span></span>
<span><span style="color: var(--shiki-color-text)">    &lt;/</span><span style="color: var(--shiki-token-string-expression)">li</span><span style="color: var(--shiki-color-text)">&gt;</span></span>
<span><span style="color: var(--shiki-color-text)">  ))</span></span>
<span><span style="color: var(--shiki-color-text)">} </span><span style="color: var(--shiki-token-keyword)">else</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">if</span><span style="color: var(--shiki-color-text)"> (isError) {</span></span>
<span><span style="color: var(--shiki-color-text)">  content </span><span style="color: var(--shiki-token-keyword)">=</span><span style="color: var(--shiki-color-text)"> &lt;</span><span style="color: var(--shiki-token-string-expression)">p</span><span style="color: var(--shiki-color-text)">&gt;{error}&lt;/</span><span style="color: var(--shiki-token-string-expression)">p</span><span style="color: var(--shiki-color-text)">&gt;</span></span>
<span><span style="color: var(--shiki-color-text)">}</span></span>
<span></span>

Optimistic Updates for Reactions

We are going to add a new method called addReaction to the postSlice. This method will perform an optimistic update when a user adds a reaction to a post, which means that we will update the UI with the new reaction before the server confirms that the reaction has been added. This helps to make the app feel faster and more responsive.

The addReaction method will be a mutation, which means that it will send a request to the server to update the post with the new reaction. The method will take two parameters: the postId of the post that the user is reacting to, and the reaction object that contains the details of the new reaction.

The URL for the addReaction mutation will be /post/:postId/, which means that we will be sending a PATCH request to update a specific post.

Overall, the addReaction method will be quite complex, as it will involve updating the state of multiple entities (the post, the user, and the reaction), as well as handling potential errors and conflicts.

We are going to add a new mutation named addReaction in the postSlice to perform optimistic update for the reactions. Here is the breakdown of the method:

  1. We want to perform optimistic update to avoid reloading the post every time a reaction is added.

  2. The addReaction mutation will take the postId and the reactions object. The URL will be /post/:postId/ and the method will be PATCH.

We will also use onQueryStarted to pass in the postId and the reaction object as the first parameter. The second parameter will take in dispatch and queryFulfilled. We will use the dispatch to call the updateQueryData function, and the queryFulfilled will return a promise which will be used to update the reactions optimistically.

The updateQueryData function requires the endpoint name and the cache key arguments, so it knows which piece of cache state to update.

We will define patchResult which will dispatch the extendedApiSlice.util.updateQueryData and pass in the endpoints, in this case, the getPosts, undefined to the cache key argument, and finally, the draft, which is the Immer-wrapped and can be "mutated" like in createSlice.

We are going to define the post, which will be the draft.entities[postId]. If the post exists, we will update the reactions.

After doing all this, we will wait for the promise to resolve and then update the post.reactions with the new reactions. Otherwise, we are going to undo this patch by calling the patchResult.undo(). This is because it's an optimistic update, which updates the cache optimistically, and we want to undo it if the promise is not resolved.

<span><span style="color: var(--shiki-color-text)">addReaction</span><span style="color: var(--shiki-token-punctuation)">:</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-constant)">builder</span><span style="color: var(--shiki-token-function)">.mutation</span><span style="color: var(--shiki-color-text)">({</span></span>
<span><span style="color: var(--shiki-color-text)">  </span><span style="color: var(--shiki-token-function)">query</span><span style="color: var(--shiki-token-keyword)">:</span><span style="color: var(--shiki-color-text)"> ({ postId</span><span style="color: var(--shiki-token-punctuation)">,</span><span style="color: var(--shiki-color-text)"> reactions }) </span><span style="color: var(--shiki-token-keyword)">=&gt;</span><span style="color: var(--shiki-color-text)"> ({</span></span>
<span><span style="color: var(--shiki-color-text)">    url</span><span style="color: var(--shiki-token-keyword)">:</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string-expression)">`posts/</span><span style="color: var(--shiki-token-keyword)">${</span><span style="color: var(--shiki-color-text)">postId</span><span style="color: var(--shiki-token-keyword)">}</span><span style="color: var(--shiki-token-string-expression)">`</span><span style="color: var(--shiki-token-punctuation)">,</span></span>
<span><span style="color: var(--shiki-color-text)">    method</span><span style="color: var(--shiki-token-keyword)">:</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string-expression)">&#39;PATCH&#39;</span><span style="color: var(--shiki-token-punctuation)">,</span></span>
<span><span style="color: var(--shiki-color-text)">    </span><span style="color: var(--shiki-token-comment)">// In a real app, we&#39;d probably need to base this on user ID somehow</span></span>
<span><span style="color: var(--shiki-color-text)">    </span><span style="color: var(--shiki-token-comment)">// so that a user can&#39;t do the same reaction more than once</span></span>
<span><span style="color: var(--shiki-color-text)">    body</span><span style="color: var(--shiki-token-keyword)">:</span><span style="color: var(--shiki-color-text)"> { reactions }</span><span style="color: var(--shiki-token-punctuation)">,</span></span>
<span><span style="color: var(--shiki-color-text)">  })</span><span style="color: var(--shiki-token-punctuation)">,</span></span>
<span><span style="color: var(--shiki-color-text)">  </span><span style="color: var(--shiki-token-keyword)">async</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-function)">onQueryStarted</span><span style="color: var(--shiki-color-text)">({ postId</span><span style="color: var(--shiki-token-punctuation)">,</span><span style="color: var(--shiki-color-text)"> reactions }</span><span style="color: var(--shiki-token-punctuation)">,</span><span style="color: var(--shiki-color-text)"> { dispatch</span><span style="color: var(--shiki-token-punctuation)">,</span><span style="color: var(--shiki-color-text)"> queryFulfilled }) {</span></span>
<span><span style="color: var(--shiki-color-text)">    </span><span style="color: var(--shiki-token-comment)">// `updateQueryData` requires the endpoint name and cache key arguments,</span></span>
<span><span style="color: var(--shiki-color-text)">    </span><span style="color: var(--shiki-token-comment)">// so it knows which piece of cache state to update</span></span>
<span><span style="color: var(--shiki-color-text)">    </span><span style="color: var(--shiki-token-keyword)">const</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-constant)">patchResult</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">=</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-function)">dispatch</span><span style="color: var(--shiki-color-text)">(</span></span>
<span><span style="color: var(--shiki-color-text)">      </span><span style="color: var(--shiki-token-constant)">extendedApiSlice</span><span style="color: var(--shiki-token-function)">.</span><span style="color: var(--shiki-token-constant)">util</span><span style="color: var(--shiki-token-function)">.updateQueryData</span><span style="color: var(--shiki-color-text)">(</span><span style="color: var(--shiki-token-string-expression)">&#39;getPosts&#39;</span><span style="color: var(--shiki-token-punctuation)">,</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-constant)">undefined</span><span style="color: var(--shiki-token-punctuation)">,</span><span style="color: var(--shiki-color-text)"> (draft) </span><span style="color: var(--shiki-token-keyword)">=&gt;</span><span style="color: var(--shiki-color-text)"> {</span></span>
<span><span style="color: var(--shiki-color-text)">        </span><span style="color: var(--shiki-token-comment)">// The `draft` is Immer-wrapped and can be &quot;mutated&quot; like in createSlice</span></span>
<span><span style="color: var(--shiki-color-text)">        </span><span style="color: var(--shiki-token-keyword)">const</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-constant)">post</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">=</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-constant)">draft</span><span style="color: var(--shiki-color-text)">.entities[postId]</span></span>
<span><span style="color: var(--shiki-color-text)">        </span><span style="color: var(--shiki-token-keyword)">if</span><span style="color: var(--shiki-color-text)"> (post) </span><span style="color: var(--shiki-token-constant)">post</span><span style="color: var(--shiki-color-text)">.reactions </span><span style="color: var(--shiki-token-keyword)">=</span><span style="color: var(--shiki-color-text)"> reactions</span></span>
<span><span style="color: var(--shiki-color-text)">      })</span></span>
<span><span style="color: var(--shiki-color-text)">    )</span></span>
<span><span style="color: var(--shiki-color-text)">    </span><span style="color: var(--shiki-token-keyword)">try</span><span style="color: var(--shiki-color-text)"> {</span></span>
<span><span style="color: var(--shiki-color-text)">      </span><span style="color: var(--shiki-token-keyword)">await</span><span style="color: var(--shiki-color-text)"> queryFulfilled</span></span>
<span><span style="color: var(--shiki-color-text)">    } </span><span style="color: var(--shiki-token-keyword)">catch</span><span style="color: var(--shiki-color-text)"> {</span></span>
<span><span style="color: var(--shiki-color-text)">      </span><span style="color: var(--shiki-token-constant)">patchResult</span><span style="color: var(--shiki-token-function)">.undo</span><span style="color: var(--shiki-color-text)">()</span></span>
<span><span style="color: var(--shiki-color-text)">    }</span></span>
<span><span style="color: var(--shiki-color-text)">  }</span><span style="color: var(--shiki-token-punctuation)">,</span></span>
<span><span style="color: var(--shiki-color-text)">})</span></span>
<span></span>

Let's export the newly create custom hook useAddReactionMutation which will be used in the ReactionButtons component.

<span><span style="color: var(--shiki-color-text)">    </span><span style="color: var(--shiki-token-keyword)">export</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">const</span><span style="color: var(--shiki-color-text)"> {</span></span>
<span><span style="color: var(--shiki-color-text)">    </span><span style="color: var(--shiki-token-constant)">useGetPostsQuery</span><span style="color: var(--shiki-token-punctuation)">,</span></span>
<span><span style="color: var(--shiki-color-text)">    </span><span style="color: var(--shiki-token-constant)">useGetPostsByUserIdQuery</span><span style="color: var(--shiki-token-punctuation)">,</span></span>
<span><span style="color: var(--shiki-color-text)">    </span><span style="color: var(--shiki-token-constant)">useAddNewPostMutation</span><span style="color: var(--shiki-token-punctuation)">,</span></span>
<span><span style="color: var(--shiki-color-text)">    </span><span style="color: var(--shiki-token-constant)">useUpdatePostMutation</span><span style="color: var(--shiki-token-punctuation)">,</span></span>
<span><span style="color: var(--shiki-color-text)">    </span><span style="color: var(--shiki-token-constant)">useDeletePostMutation</span><span style="color: var(--shiki-token-punctuation)">,</span></span>
<span><span style="color: var(--shiki-color-text)">    ⬇️⬇️⬇️⬇️⬇️⬇️⬇️</span></span>
<span><span style="color: var(--shiki-color-text)">    </span><span style="color: var(--shiki-token-constant)">useAddReactionMutation</span></span>
<span><span style="color: var(--shiki-color-text)">} </span><span style="color: var(--shiki-token-keyword)">=</span><span style="color: var(--shiki-color-text)"> extendedApiSlice</span></span>
<span></span>
<span></span>

ReactionButtons component

The final step is to update the ReactionButtons component. We will begin by importing the useAddReactionMutation mutation from the postSlice. This mutation will be used to add a new reaction to a post.

<span><span style="color: var(--shiki-token-keyword)">import</span><span style="color: var(--shiki-color-text)"> { useAddReactionMutation } </span><span style="color: var(--shiki-token-keyword)">from</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string-expression)">&#39;./postsSlice&#39;</span></span>
<span></span>

Next we are going to add the addReaction function from useAddReactionMutation mutation.

<span><span style="color: var(--shiki-token-keyword)">const</span><span style="color: var(--shiki-color-text)"> [</span><span style="color: var(--shiki-token-constant)">addReaction</span><span style="color: var(--shiki-color-text)">] </span><span style="color: var(--shiki-token-keyword)">=</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-function)">useAddReactionMutation</span><span style="color: var(--shiki-color-text)">()</span></span>
<span></span>

We will replace the onClick with the addReaction function and pass in the postId and the reactions object. Inside the addReaction function, we will define a new value as post.reactions[name] + 1. We will then spread the post.reactions and add the new value to it. Finally, we will overwrite the reactions object with the new value for that specific reaction.

<span><span style="color: var(--shiki-color-text)">&lt;</span><span style="color: var(--shiki-token-string-expression)">button</span></span>
<span><span style="color: var(--shiki-color-text)">  </span><span style="color: var(--shiki-token-function)">key</span><span style="color: var(--shiki-token-keyword)">=</span><span style="color: var(--shiki-color-text)">{name}</span></span>
<span><span style="color: var(--shiki-color-text)">  </span><span style="color: var(--shiki-token-function)">type</span><span style="color: var(--shiki-token-keyword)">=</span><span style="color: var(--shiki-token-string-expression)">&quot;button&quot;</span></span>
<span><span style="color: var(--shiki-color-text)">  </span><span style="color: var(--shiki-token-function)">className</span><span style="color: var(--shiki-token-keyword)">=</span><span style="color: var(--shiki-token-string-expression)">&quot;reactionButton&quot;</span></span>
<span><span style="color: var(--shiki-color-text)">  </span><span style="color: var(--shiki-token-function)">onClick</span><span style="color: var(--shiki-token-keyword)">=</span><span style="color: var(--shiki-color-text)">{() </span><span style="color: var(--shiki-token-keyword)">=&gt;</span><span style="color: var(--shiki-color-text)"> {</span></span>
<span><span style="color: var(--shiki-color-text)">    </span><span style="color: var(--shiki-token-keyword)">const</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-constant)">newValue</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">=</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-constant)">post</span><span style="color: var(--shiki-color-text)">.reactions[name] </span><span style="color: var(--shiki-token-keyword)">+</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-constant)">1</span></span>
<span><span style="color: var(--shiki-color-text)">    </span><span style="color: var(--shiki-token-function)">addReaction</span><span style="color: var(--shiki-color-text)">({</span></span>
<span><span style="color: var(--shiki-color-text)">      postId</span><span style="color: var(--shiki-token-keyword)">:</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-constant)">post</span><span style="color: var(--shiki-color-text)">.id</span><span style="color: var(--shiki-token-punctuation)">,</span></span>
<span><span style="color: var(--shiki-color-text)">      reactions</span><span style="color: var(--shiki-token-keyword)">:</span><span style="color: var(--shiki-color-text)"> { </span><span style="color: var(--shiki-token-keyword)">...</span><span style="color: var(--shiki-token-constant)">post</span><span style="color: var(--shiki-color-text)">.reactions</span><span style="color: var(--shiki-token-punctuation)">,</span><span style="color: var(--shiki-color-text)"> [name]</span><span style="color: var(--shiki-token-keyword)">:</span><span style="color: var(--shiki-color-text)"> newValue }</span><span style="color: var(--shiki-token-punctuation)">,</span></span>
<span><span style="color: var(--shiki-color-text)">    })</span></span>
<span><span style="color: var(--shiki-color-text)">  }}</span></span>
<span><span style="color: var(--shiki-color-text)">&gt;</span></span>
<span><span style="color: var(--shiki-color-text)">  {emoji} {</span><span style="color: var(--shiki-token-constant)">post</span><span style="color: var(--shiki-color-text)">.reactions[name]}</span></span>
<span><span style="color: var(--shiki-color-text)">&lt;/</span><span style="color: var(--shiki-token-string-expression)">button</span><span style="color: var(--shiki-color-text)">&gt;</span></span>
<span></span>

Let's test it out, we are going to add a reaction to the post and we can see that the reaction is added instantly. Head over to the terminal and run

<span><span style="color: var(--shiki-color-text)">npm </span><span style="color: var(--shiki-token-string)">run</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string)">start</span></span>
<span></span>

Make sure that the server is up and running to ensure that this works properly. Once you have done that, you can fire up the blog and verify that everything is working as expected. Try adding some reactions to the post and make sure that they are being added correctly.

View Redux state cache and network requests with dev tools

Let's open up our dev tools and navigate to the network tab. We should see a network request for each request and notice that it updates immediately. This is what we call an optimistic update, which happens before hitting the server.

Conclusion

In summary, this tutorial has covered a lot of ground with RTK Query. We have implemented a normalized state and optimistic updates. Congratulations on making it this far and I hope you found this tutorial helpful. If you have any questions, feel free to reach out to me via email below. I will be happy to answer any inquiries you may have.