Client side update with RTK Query
Tags: react, redux
Published: 2023-02-11 20:03:49

This is my memo from when I delved into Redux Toolkit Query (RTK query) and discovered how to update the Redux state created by RTK query solely on the client side.

This article is not intended to advocate for always using RTK query for managing state solely on the client side. In my opinion, the most common use of RTK query is to communicate frequently with the server side, taking advantage of features such as [default cache storage behavior](https://redux-toolkit.js.org/rtk-query/usage/cache-behavior#default-cache-behavior) and [optimistic updates](https://redux-toolkit.js.org/rtk-query/usage/manual-cache-updates#optimistic-updates).

Anyway, I was interested in understanding how I could update the state only on the client side, just like with normal Redux, which is why I decided to write this article.

## Basic usage

Please read the official document [here](https://redux-toolkit.js.org/rtk-query/overview#basic-usage).

Assuming we have the following files:

![folder structure](https://cdn-images-1.medium.com/max/2000/1*e9ZX3ebHIm0Rsv-l5dSC_Q.png)

### starWars-api.ts
```typescript
    import { createApi, fetchBaseQuery } from "@reduxjs/toolkit/query/react";
    import {
      RowCharacter,
      CategorizedCharacters,
      ModifiedCharacter
    } from "./starWars-types";
    import { modifyRowCharacters, categorizeCharacters } from "./starWars-utils";
    
    export const starWarsApi = createApi({
      reducerPath: "starWarsApi",
      baseQuery: fetchBaseQuery({
        baseUrl: "https://akabab.github.io/starwars-api/api/"
      }),
      endpoints: (builder) => ({
        getCharacters: builder.query<CategorizedCharacters, undefined>({
          query: () => "all.json",
          transformResponse: (response: RowCharacter[]): CategorizedCharacters => {
            if (response.length === 0) return {};
            const modifiedCharacters = modifyRowCharacters(response);
            const categorizedCharacters = categorizeCharacters(modifiedCharacters);
            return { all: modifiedCharacters, ...categorizedCharacters };
          }
        })
      })
    });
```
I hope to write another article about transformResponselater soon.

### store.js
```typescript
    import { useDispatch } from "react-redux";
    import { configureStore } from "@reduxjs/toolkit";
    import { setupListeners } from "@reduxjs/toolkit/query";
    import { starWarsApi } from "./api/starWars-api";
    
    export const store = configureStore({
      reducer: {
        [starWarsApi.reducerPath]: starWarsApi.reducer
      },
      middleware: (getDefaultMiddleware) =>
        getDefaultMiddleware().concat(starWarsApi.middleware)
    });
    
    setupListeners(store.dispatch);
```
💡You can use [ApiProvider](https://redux-toolkit.js.org/rtk-query/api/ApiProvider) if you haven’t set up a Redux store in your project. In that case, you don’t need to setup Redux store. I haven’t yet determined if I can use the ApiProvider instead of the Provider in this project.

### App.js
```typescript
    import { isEmpty } from "lodash";
    import { useAppDispatch } from "./store";
    import { useGetCharactersQuery, updateCharacter } from "./api/starWars-api";
    
    import CharacterList from "./components/CharacterList";
    
    import "./styles.css";
    
    export default function App() {
      const dispatch = useAppDispatch();
      const { data, error, isLoading } = useGetCharactersQuery(undefined);
      if (isLoading) return <div>Loading</div>;
      if (error) return <div>Error</div>;
      if (!data || isEmpty(data)) return <div>No data found</div>;
    
      const { all, ...charactersBySpecies } = data;
    
      return (
        <div className="App">
          {Object.keys(charactersBySpecies).map((key) => (
            <CharacterList
              key={key}
              name={key}
              characters={charactersBySpecies[key]}
            />
          ))}
        </div>
      );
    }
```
## Recipe

I implemented this updating logic using [updateQueryData](https://redux-toolkit.js.org/rtk-query/api/created-api/api-slice-utils#updatequerydata) .

### store.js

Set up custom useDispatch in store.js.
💡This is TypeScript specific process.
```typescript
    export type AppDispatch = typeof store.dispatch;
    export const useAppDispatch: () => AppDispatch = useDispatch;
```
### starWars-api.ts

I added an action-like function, following [the example](https://redux-toolkit.js.org/rtk-query/api/created-api/api-slice-utils#example-1) in the official documentation.
```typescript
    export const updateCharacter = (characters: ModifiedCharacter[]) =>
      starWarsApi.util.updateQueryData(
        "getCharacters",
        undefined,
        (draftPosts) => ({
          new: characters,
          ...draftPosts
        })
      );
    export const { useGetCharactersQuery } = starWarsApi;
```
### App.js

Use updateCharacter in App.js.
```typescript
    import { isEmpty } from "lodash";
    import { useAppDispatch } from "./store";
    import { useGetCharactersQuery, updateCharacter } from "./api/starWars-api";
    
    import CharacterList from "./components/CharacterList";
    
    import "./styles.css";
    
    export default function App() {
      const dispatch = useAppDispatch();
      const { data, error, isLoading } = useGetCharactersQuery(undefined);
      if (isLoading) return <div>Loading</div>;
      if (error) return <div>Error</div>;
      if (!data || isEmpty(data)) return <div>No data found</div>;
    
      const { all, ...charactersBySpecies } = data;
      const handleClick = () =>
        dispatch(
          updateCharacter([
            {
              id: 0,
              name: "name",
              image: "image",
              gender: "gender",
              species: "speceis"
            }
          ])
        );
    
      return (
        <div className="App">
          <button onClick={handleClick}>click</button>
          {Object.keys(charactersBySpecies).map((key) => (
            <CharacterList
              key={key}
              name={key}
              characters={charactersBySpecies[key]}
            />
          ))}
        </div>
      );
```
That’s it. Thank you for reading :)

The entire code is available [here](https://codesandbox.io/s/elated-morning-62jvcs).
The original article is [here](https://medium.com/@lada496/client-side-update-with-redux-toolkit-query-8f8edb984d4d)