# Technical Documentation: reduxjs/redux-toolkit

> ℹ️ **Provenance:** Multi-Tier Ingestion · [CodeWiki Reference](https://codewiki.google/github.com/reduxjs/redux-toolkit)

### Overview (`docs/README.md`)

# Redux Toolkit docs

The pages in this folder are published as part of the combined Redux docs site, at https://redux.js.org/toolkit. The site itself (Docusaurus config, theme, search, redirects, build scripts) lives in the [`reduxjs/redux`](https://github.com/reduxjs/redux) repo under `website/`. This folder holds only the Redux Toolkit pages.

## What lives where

| What                                                                 | Where                                       |
| -------------------------------------------------------------------- | ------------------------------------------- |
| Redux Toolkit pages                                                  | `docs/**/*.{md,mdx}` in this repo           |
| Redux Toolkit sidebar                                                | `docs/sidebars.ts` in this repo             |
| Images used by these pages                                           | `docs/assets/`, referenced by relative path |
| RTK Query internals notes for maintainers (not published)            | `docs/rtk-query/internal/`                  |
| Tutorials, TypeScript setup, style guide, FAQ, troubleshooting       | `docs/` in `reduxjs/redux`                  |
| Site config, navbar, theme, search, redirects (`website/_redirects`) | `website/` in `reduxjs/redux`               |

Topics shared by all the Redux libraries are written once, in the core docs. Link to them instead of repeating them here.

## Links

- Other Redux Toolkit pages: a relative file link (`../api/createSlice.mdx`) or a site path (`/toolkit/api/createSlice`).
- Other libraries and the core docs: site paths, such as `/usage/usage-with-typescript`, `/react-redux/api/hooks`, or `/reselect/api/createSelector`. Do not use full `https://redux.js.org/...` URLs.
- When you rename, move, or delete a page, add a redirect for the old URL to `website/_redirects` in `reduxjs/redux`.

## Sidebar

New pages only appear in the navigation once they are listed in `docs/sidebars.ts`. The file has no imports because the site loads it from a copy of this folder. Its local `SidebarItem` type catches misspelled keys:

```bash
pnpm exec tsc -p docs/tsconfig.json --noEmit
```

The site build checks that every doc id in the sidebar exists.

## Code blocks

TypeScript code blocks in `.mdx` files are type-checked during the site build, and a type error fails the build. They check against the `dist` folder of `packages/toolkit` when it has been built, and against the published `@reduxjs/toolkit` otherwise. Deploy previews build the package first, so a PR's code blocks check against that PR's source.

In the build log, a type error shows up as `Module build failed (from ./plugins/toolkit-types-dependency.cjs)`. That loader only tracks dependencies; the lines after it name the page, the code block, and the TypeScript error.

API pages also pull doc comments from `packages/toolkit/src`, and the `/toolkit/errors` page (`docs/api/errors.mdx`) renders `errors.json`, so changes to either show up on the site.

## Previewing a PR

Every PR that changes `docs/`, `packages/toolkit/src/`, or `errors.json` gets a Netlify deploy preview of the whole combined site, with this branch's Redux Toolkit docs in place of the published ones.

## Previewing locally

Clone `reduxjs/redux` next to this repo and install the site's dependencies once:

```bash
git clone https://github.com/reduxjs/redux.git ../redux
cd ../redux/docs && pnpm install
cd ../website && pnpm install
```

Then start the dev server from `../redux/website`, pointing it at this checkout:

```bash
DOCS_SOURCE_REDUX_TOOLKIT=../../redux-toolkit pnpm dev
```

`pnpm dev` copies this repo's docs into `website/external/redux-toolkit`, copies each file again when it changes, and runs `docusaurus start`. The other libraries are cloned from GitHub. Set `DOCS_SOURCE_REACT_REDUX` or `DOCS_SOURCE_RESELECT` as well to use local checkouts of those. To type-check code blocks against local source changes, run `pnpm build` in `packages/toolkit` before starting.

The dev server does not report broken links. To run the same checks as a deploy preview:

```bash
DOCS_SOURCE_REDUX_TOOLKIT=../../redux-toolkit pnpm fetch-docs --force
pnpm build
```

---

### Overview (`packages/rtk-codemods/README.md`)

# RTK Codemods

A collection of codemods for updating legacy Redux Toolkit API usage patterns to modern patterns.

## Usage

To run a specific codemod from this project, you would run the following:

```bash
npx @reduxjs/rtk-codemods <TRANSFORM NAME> path/of/files/ or/some**/*glob.js

# or

yarn global add @reduxjs/rtk-codemods
rtk-codemods <TRANSFORM NAME> path/of/files/ or/some**/*glob.js
```

## Local Usage

```
node ./bin/cli.mjs <TRANSFORM NAME> path/of/files/ or/some**/*glob.js
```

## Transforms



- [createReducerBuilder](transforms/createReducerBuilder/README.md)
- [createSliceBuilder](transforms/createSliceBuilder/README.md)
- [createSliceReducerBuilder](transforms/createSliceReducerBuilder/README.md)



## Contributing

### Installation

- clone the repo
- change into the repo directory
- `pnpm install`

### Running tests

- `pnpm --filter @reduxjs/rtk-codemods test`

---

### Overview (`packages/rtk-query-codegen-openapi/README.md`)

<p align="center">
  <img src="https://raw.githubusercontent.com/rtk-incubator/rtk-query/main/logo.png" width="400" />
</p>
<h2 align="center">
Code Generator
</h2>

<p align="center">
   <a href="https://discord.gg/0ZcbPKXt5bZ6au5t" target="_blank">
    <img src="https://img.shields.io/badge/chat-online-green" alt="Discord server" />
  </a>
</p>

### Introduction

This is a utility library meant to be used with [RTK Query](https://redux-toolkit.js.org/rtk-query/overview) that will generate a typed API client from an OpenAPI schema.

### Documentation

[View the RTK Query Code Generation docs](https://redux-toolkit.js.org/rtk-query/usage/code-generation)

If you use an AI agent, run `npx @tanstack/intent@latest install` to install agent skills.

---

### Overview (`packages/toolkit/README.md`)

# Redux Toolkit


[](https://www.npmjs.com/package/@reduxjs/toolkit)
[](https://www.npmjs.com/package/@reduxjs/toolkit)

**The official, opinionated, batteries-included toolset for efficient Redux development**

## Installation

### Create a React Redux App

The recommended way to start new apps with React and Redux Toolkit is by using [our official Redux Toolkit + TS template for Vite](https://github.com/reduxjs/redux-templates), or by creating a new Next.js project using [Next's `with-redux` template](https://github.com/vercel/next.js/tree/canary/examples/with-redux).

Both of these already have Redux Toolkit and React-Redux configured appropriately for that build tool, and come with a small example app that demonstrates how to use several of Redux Toolkit's features.

```bash
# Vite with our Redux+TS template
# (using the `tiged` tool to clone and extract the template)
npx tiged reduxjs/redux-templates/packages/vite-template-redux my-app

# Next.js using the `with-redux` template
npx create-next-app --example with-redux my-app
```

For React Native, we have [an official Redux+TS template for Expo](https://github.com/reduxjs/redux-templates/tree/master/packages/expo-template-redux-typescript):

```bash
npx tiged reduxjs/redux-templates/packages/expo-template-redux-typescript my-app
```

### An Existing App

Redux Toolkit is available as a package on NPM for use with a module bundler or in a Node application:

```bash
# NPM
npm install @reduxjs/toolkit

# Yarn
yarn add @reduxjs/toolkit
```

If you use an AI agent, run `npx @tanstack/intent@latest install` to install agent skills.

The package includes a precompiled browser ESM build that can be loaded from a `<script type="module">` tag. No-bundler browser usage also needs an import map, because the build imports Redux Toolkit's dependencies by package name. See the [Getting Started docs](https://redux-toolkit.js.org/introduction/getting-started#an-existing-app) for an example.

## Documentation

The Redux Toolkit docs are available at **https://redux-toolkit.js.org**, including API references and usage guides for all of the APIs included in Redux Toolkit.

The Redux core docs at https://redux.js.org includes the full Redux tutorials, as well usage guides on general Redux patterns.

## Purpose

The **Redux Toolkit** package is intended to be the standard way to write Redux logic. It was originally created to help address three common concerns about Redux:

- "Configuring a Redux store is too complicated"
- "I have to add a lot of packages to get Redux to do anything useful"
- "Redux requires too much boilerplate code"

We can't solve every use case, but in the spirit of [`create-react-app`](https://github.com/facebook/create-react-app), we can try to provide some tools that abstract over the setup process and handle the most common use cases, as well as include some useful utilities that will let the user simplify their application code.

Because of that, this package is deliberately limited in scope. It does _not_ address concepts like "reusable encapsulated Redux modules", folder or file structures, managing entity relationships in the store, and so on.

Redux Toolkit also includes a powerful data fetching and caching capability that we've dubbed "RTK Query". It's included in the package as a separate set of entry points. It's optional, but can eliminate the need to hand-write data fetching logic yourself.

## What's Included

Redux Toolkit includes these APIs:

- `configureStore()`: wraps `createStore` to provide simplified configuration options and good defaults. It can automatically combine your slice reducers, add whatever Redux middleware you supply, includes `redux-thunk` by default, and enables use of the Redux DevTools Extension.
- `createReducer()`: lets you supply a lookup table of action types to case reducer functions, rather than writing switch statements. In addition, it automatically uses the [`immer` library](https://github.com/mweststrate/immer) to let you write simpler immutable updates with normal mutative code, like `state.todos[3].completed = true`.
- `createAction()`: generates an action creator function for the given action type string. The function itself has `toString()` defined, so that it can be used in place of the type constant.
- `createSlice()`: combines `createReducer()` + `createAction()`. Accepts an object of reducer functions, a slice name, and an initial state value, and automatically generates a slice reducer with corresponding action creators and action types.
- `combineSlices()`: combines multiple slices into a single reducer, and allows "lazy loading" of slices after initialisation.
- `createListenerMiddleware()`: lets you define "listener" entries that contain an "effect" callback with additional logic, and a way to specify when that callback should run based on dispatched actions or state changes. A lightweight alternative to Redux async middleware like sagas and observables.
- `createAsyncThunk()`: accepts an action type string and a function that returns a promise, and generates a thunk that dispatches `pending/resolved/rejected` action types based on that promise
- `createEntityAdapter()`: generates a set of reusable reducers and selectors to manage normalized data in the store
- The `createSelector()` utility from the [Reselect](https://github.com/reduxjs/reselect) library, re-exported for ease of use.

For details, see [the Redux Toolkit API Reference section in the docs](https://redux-toolkit.js.org/api/configureStore).

## RTK Query

**RTK Query** is provided as an optional addon within the `@reduxjs/toolkit` package. It is purpose-built to solve the use case of data fetching and caching, supplying a compact, but powerful toolset to define an API interface layer for your app. It is intended to simplify common cases for loading data in a web application, eliminating the need to hand-write data fetching & caching logic yourself.

RTK Query is built on top of the Redux Toolkit core for its implementation, using [Redux](https://redux.js.org/) internally for its architecture. Although knowledge of Redux and RTK are not required to use RTK Query, you should explore all of the additional global store management capabilities they provide, as well as installing the [Redux DevTools browser extension](https://github.com/reduxjs/redux-devtools), which works flawlessly with RTK Query to traverse and replay a timeline of your request & cache behavior.

RTK Query is included within the installation of the core Redux Toolkit package. It is available via either of the two entry points below:

```ts no-transpile
import { createApi } from '@reduxjs/toolkit/query'

/* React-specific entry point that automatically generates
   hooks corresponding to the defined endpoints */
import { createApi } from '@reduxjs/toolkit/query/react'
```

### What's included

RTK Query includes these APIs:

- `createApi()`: The core of RTK Query's functionality. It allows you to define a set of endpoints describe how to retrieve data from a series of endpoints, including configuration of how to fetch and transform that data. In most cases, you should use this once per app, with "one API slice per base URL" as a rule of thumb.
- `fetchBaseQuery()`: A small wrapper around fetch that aims to simplify requests. Intended as the recommended baseQuery to be used in createApi for the majority of users.
- `<ApiProvider />`: Can be used as a Provider if you do not already have a Redux store.
- `setupListeners()`: A utility used to enable refetchOnMount and refetchOnReconnect behaviors.

See the [**RTK Query Overview**](https://redux-toolkit.js.org/rtk-query/overview) page for more details on what RTK Query is, what problems it solves, and how to use it.

## Contributing

Please refer to our [contributing guide](/CONTRIBUTING.md) to learn about our development process, how to propose bugfixes and improvements, and how to build and test your changes to Redux Toolkit.

---

### Overview (`examples/publish-ci/expo/README.md`)

# Expo Template Redux TypeScript

The official Redux+TS template for Expo.

## :arrow_forward: Usage

```sh
npx create-expo my-app --template expo-template-redux-typescript
```

# Getting Started

> **Note**: Make sure you have completed the [React Native - Environment Setup](https://reactnative.dev/docs/environment-setup) instructions till "Creating a new application" step, before proceeding.

## Step 1: Start the Metro Server

First, you will need to start **Metro**, the JavaScript _bundler_ that ships _with_ React Native.

To start Metro, run the following command from the _root_ of your React Native project:

```bash
# using npm
npm start
```

```bash
# OR using Yarn
pnpm start
```

## Step 2: Start your Application

Let Metro Bundler run in its _own_ terminal. Open a _new_ terminal from the _root_ of your React Native project. Run the following command to start your _Android_ or _iOS_ app:

### For Android

```bash
# using npm
npm run android
```

```bash
# OR using Yarn
pnpm android
```

### For iOS

```bash
# using npm
npm run ios
```

```bash
# OR using Yarn
pnpm ios
```

If everything is set up _correctly_, you should see your new app running in your _Android Emulator_ or _iOS Simulator_ shortly provided you have set up your emulator/simulator correctly.

This is one way to run your app — you can also run it directly from within Android Studio and Xcode respectively.

## Step 3: Modifying your App

Now that you have successfully run the app, let's modify it.

1. Open `App.tsx` in your text editor of choice and edit some lines.
2. For **Android**: Press the <kbd>R</kbd> key twice or select **"Reload"** from the **Developer Menu** (<kbd>Ctrl</kbd> + <kbd>M</kbd> (on Window and Linux) or <kbd>Cmd ⌘</kbd> + <kbd>M</kbd> (on macOS)) to see your changes!

   For **iOS**: Hit <kbd>Cmd ⌘</kbd> + <kbd>R</kbd> in your iOS Simulator to reload the app and see your changes!

## Congratulations! :tada:

You've successfully run and modified your Expo App. :partying_face:

---

### Overview (`examples/publish-ci/next/README.md`)

# Redux Toolkit + Next.js App Router CI example

This is a CI fixture, not a starter template. It exists so that RTK's
`test-published-artifact` job can install a freshly packed `@reduxjs/toolkit`
tarball into a real Next.js app, build it, and run a Playwright test against it.

## What it covers

- Next 16 with the **App Router** and Turbopack, on React 19.
- The client half of the app resolves RTK through Next's bundler, which picks
  the `module-sync` / `module` condition and lands on
  `dist/redux-toolkit.modern.mjs`.
- `src/app/ServerApiInfo.tsx` is a **Server Component** that imports the core
  RTK Query entry point (`@reduxjs/toolkit/query`, not `/query/react`) and
  builds a store on the server. This is the only place in the RTK repo that
  exercises RTK inside the React Server Components graph. If RTK ever needs a
  `react-server` export condition, this is where it should show up first.
- `src/app/StoreProvider.tsx` creates the store per client instance with a lazy
  `useState` initializer, matching the pattern in RTK's
  [Next.js setup docs](https://redux-toolkit.js.org/usage/nextjs).

## Layout

| Path                | What it is                                             |
| ------------------- | ------------------------------------------------------ |
| `src/app/`          | App Router entry points and the client/server boundary |
| `src/app-core/`     | Store, typed hooks, and RTK Query API definitions      |
| `src/features/`     | Shared app code, kept in sync with the `vite` example  |
| `src/mocks/`        | MSW handlers and browser worker                        |
| `tests/playwright/` | The single end-to-end test CI runs                     |

## Scripts

```bash
pnpm dev         # next dev
pnpm build       # next build
pnpm type-check  # tsc
pnpm test        # build must have run first; starts the server and runs Playwright
```

---

### Overview (`examples/publish-ci/node/README.md`)

# Node resolution example

Checks that a published `@reduxjs/toolkit` tarball resolves and runs correctly
under plain Node, with no bundler involved.

There are three test modes, one per way Node can load RTK:

| Script             | How RTK is loaded                      | Build it should reach  |
| ------------------ | -------------------------------------- | ---------------------- |
| `test:esm`         | `import` from an ES module             | `*.modern.mjs`         |
| `test:require-esm` | `require()` with require(esm) enabled  | `*.modern.mjs`         |
| `test:require-cjs` | `require()` with require(esm) disabled | `dist/**/cjs/index.js` |

`require(esm)` is on by default from Node 22.12 onward. When it is on, Node adds
the `module-sync` export condition for `require()` calls, so a CommonJS caller
gets the ESM build. `test:require-cjs` passes `--no-experimental-require-module`
to turn that off, which both exercises the real CommonJS build and reproduces
how Node versions older than 22.12 resolve RTK.

Each mode covers all four entry points — `@reduxjs/toolkit`,
`@reduxjs/toolkit/react`, `@reduxjs/toolkit/query`, and
`@reduxjs/toolkit/query/react`. For each one it asserts the resolved file path
and then runs a small behavior check, so a wrong-but-loadable build fails here
rather than passing silently.

`pnpm test` runs all three.

---

### Overview (`examples/publish-ci/react-native/README.md`)

This is a new [**React Native**](https://reactnative.dev) project, bootstrapped using [`@react-native-community/cli`](https://github.com/react-native-community/cli).

# Getting Started

> **Note**: Make sure you have completed the [React Native - Environment Setup](https://reactnative.dev/docs/environment-setup) instructions till "Creating a new application" step, before proceeding.

## Step 1: Start the Metro Server

First, you will need to start **Metro**, the JavaScript _bundler_ that ships _with_ React Native.

To start Metro, run the following command from the _root_ of your React Native project:

```bash
# using npm
npm start

# OR using Yarn
pnpm start
```

## Step 2: Start your Application

Let Metro Bundler run in its _own_ terminal. Open a _new_ terminal from the _root_ of your React Native project. Run the following command to start your _Android_ or _iOS_ app:

### For Android

```bash
# using npm
npm run android

# OR using Yarn
pnpm android
```

### For iOS

```bash
# using npm
npm run ios

# OR using Yarn
pnpm ios
```

If everything is set up _correctly_, you should see your new app running in your _Android Emulator_ or _iOS Simulator_ shortly provided you have set up your emulator/simulator correctly.

This is one way to run your app — you can also run it directly from within Android Studio and Xcode respectively.

## Step 3: Modifying your App

Now that you have successfully run the app, let's modify it.

1. Open `App.tsx` in your text editor of choice and edit some lines.
2. For **Android**: Press the <kbd>R</kbd> key twice or select **"Reload"** from the **Developer Menu** (<kbd>Ctrl</kbd> + <kbd>M</kbd> (on Window and Linux) or <kbd>Cmd ⌘</kbd> + <kbd>M</kbd> (on macOS)) to see your changes!

   For **iOS**: Hit <kbd>Cmd ⌘</kbd> + <kbd>R</kbd> in your iOS Simulator to reload the app and see your changes!

## Congratulations! :tada:

You've successfully run and modified your React Native App. :partying_face:

### Now what?

- If you want to add this new React Native code to an existing application, check out the [Integration guide](https://reactnative.dev/docs/integration-with-existing-apps).
- If you're curious to learn more about React Native, check out the [Introduction to React Native](https://reactnative.dev/docs/getting-started).

# Troubleshooting

If you can't get this to work, see the [Troubleshooting](https://reactnative.dev/docs/troubleshooting) page.

# Learn More

To learn more about React Native, take a look at the following resources:

- [React Native Website](https://reactnative.dev) - learn more about React Native.
- [Getting Started](https://reactnative.dev/docs/environment-setup) - an **overview** of React Native and how setup your environment.
- [Learn the Basics](https://reactnative.dev/docs/getting-started) - a **guided tour** of the React Native **basics**.
- [Blog](https://reactnative.dev/blog) - read the latest official React Native **Blog** posts.
- [`@facebook/react-native`](https://github.com/facebook/react-native) - the Open Source; GitHub **repository** for React Native.

---

### Overview (`examples/query/react/infinite-queries/README.md`)

# RTK Query Infinite Queries Example

This example shows a variety of usage patterns for RTK Query's infinite query endpoint support, including:

- Basic pagination
- Infinite scrolling
- Bidirectional cursors
- Offset + limit
- Max pages
- React Native FlatList

---

### Overview (`packages/rtk-codemods/transforms/createReducerBuilder/README.md`)

# createReducerBuilder

Rewrites uses of the "object" syntax for Redux Toolkit's `createReducer` API to use the "builder callback" syntax instead, in preparation for removal of the object syntax in RTK 2.0.

Should work with both JS and TS files.

## Usage

```bash
npx @reduxjs/rtk-codemods createReducerBuilder path/of/files/ or/some**/*glob.js

# or

yarn global add @reduxjs/rtk-codemods
rtk-codemods createReducerBuilder path/of/files/ or/some**/*glob.js
```

## Local Usage

```
node ./bin/cli.mjs createReducerBuilder path/of/files/ or/some**/*glob.js
```

## Input / Output



- [basic-ts](#basic-ts)
- [basic](#basic)



## 

<a id="basic-ts">**basic-ts**</a>

**Input** (<small>[basic-ts.input.ts](transforms\createReducerBuilder__testfixtures__\basic-ts.input.ts)</small>):

```ts
createReducer(initialState, {
  [todoAdded]: (state: SliceState, action: PayloadAction<string>) => {
    // stuff
  }
})

createReducer(initialState, {
  [todoAdded](state: SliceState, action: PayloadAction<string>) {
    // stuff
  }
})
```

**Output** (<small>[basic-ts.output.ts](transforms\createReducerBuilder__testfixtures__\basic-ts.output.ts)</small>):

```ts
createReducer(initialState, (builder) => {
  builder.addCase(
    todoAdded,
    (state: SliceState, action: PayloadAction<string>) => {
      // stuff
    }
  )
})

createReducer(initialState, (builder) => {
  builder.addCase(
    todoAdded,
    (state: SliceState, action: PayloadAction<string>) => {
      // stuff
    }
  )
})
```

---

<a id="basic">**basic**</a>

**Input** (<small>[basic.input.js](transforms\createReducerBuilder__testfixtures__\basic.input.js)</small>):

```js
createReducer(initialState, {
  [todoAdded1a]: (state, action) => {
    // stuff
  },
  [todoAdded1b]: (state, action) => action.payload,
  [todoAdded1c + 'test']: (state, action) => {
    // stuff
  },
  [todoAdded1d](state, action) {
    // stuff
  },
  [todoAdded1e]: function (state, action) {
    // stuff
  },
  todoAdded1f: (state, action) => {
    //stuff
  }
})

createReducer(initialState, {
  [todoAdded2a]: (state, action) => {
    // stuff
  },
  [todoAdded2b](state, action) {
    // stuff
  },
  [todoAdded2c]: function (state, action) {
    // stuff
  }
})
```

**Output** (<small>[basic.output.js](transforms\createReducerBuilder__testfixtures__\basic.output.js)</small>):

```js
createReducer(initialState, (builder) => {
  builder.addCase(todoAdded1a, (state, action) => {
    // stuff
  })

  builder.addCase(todoAdded1b, (state, action) => action.payload)

  builder.addCase(todoAdded1c + 'test', (state, action) => {
    // stuff
  })

  builder.addCase(todoAdded1d, (state, action) => {
    // stuff
  })

  builder.addCase(todoAdded1e, (state, action) => {
    // stuff
  })

  builder.addCase(todoAdded1f, (state, action) => {
    //stuff
  })
})

createReducer(initialState, (builder) => {
  builder.addCase(todoAdded2a, (state, action) => {
    // stuff
  })

  builder.addCase(todoAdded2b, (state, action) => {
    // stuff
  })

  builder.addCase(todoAdded2c, (state, action) => {
    // stuff
  })
})
```

---

### Overview (`packages/rtk-codemods/transforms/createSliceBuilder/README.md`)

# createSliceBuilder

Rewrites uses of the `extraReducers` "object" syntax for Redux Toolkit's `createSlice` API to use the "builder callback" syntax instead, in preparation for removal of the object syntax in RTK 2.0.

Should work with both JS and TS files.

## Usage

```bash
npx @reduxjs/rtk-codemods createSliceBuilder path/of/files/ or/some**/*glob.js

# or

yarn global add @reduxjs/rtk-codemods
rtk-codemods createSliceBuilder path/of/files/ or/some**/*glob.js
```

## Local Usage

```
node ./bin/cli.mjs createSliceBuilder path/of/files/ or/some**/*glob.js
```

## Input / Output



- [basic-ts](#basic-ts)
- [basic](#basic)



## 

<a id="basic-ts">**basic-ts**</a>

**Input** (<small>[basic-ts.input.ts](transforms\createSliceBuilder__testfixtures__\basic-ts.input.ts)</small>):

```ts
const slice1 = createSlice({
  name: 'a',
  initialState,
  extraReducers: {
    [todoAdded]: (state: SliceState, action: PayloadAction<string>) => {
      // stuff
    }
  }
})

const slice2 = createSlice({
  name: 'b',
  initialState,
  extraReducers: {
    [todoAdded](state: SliceState, action: PayloadAction<string>) {
      // stuff
    }
  }
})
```

**Output** (<small>[basic-ts.output.ts](transforms\createSliceBuilder__testfixtures__\basic-ts.output.ts)</small>):

```ts
const slice1 = createSlice({
  name: 'a',
  initialState,

  extraReducers: (builder) => {
    builder.addCase(
      todoAdded,
      (state: SliceState, action: PayloadAction<string>) => {
        // stuff
      }
    )
  }
})

const slice2 = createSlice({
  name: 'b',
  initialState,

  extraReducers: (builder) => {
    builder.addCase(
      todoAdded,
      (state: SliceState, action: PayloadAction<string>) => {
        // stuff
      }
    )
  }
})
```

---

<a id="basic">**basic**</a>

**Input** (<small>[basic.input.js](transforms\createSliceBuilder__testfixtures__\basic.input.js)</small>):

```js
const slice1 = createSlice({
  name: 'a',
  initialState: {},
  extraReducers: {
    [todoAdded1a]: (state, action) => {
      // stuff
    },
    [todoAdded1b]: (state, action) => action.payload,
    [todoAdded1c + 'test']: (state, action) => {
      // stuff
    },
    [todoAdded1d](state, action) {
      // stuff
    },
    [todoAdded1e]: function (state, action) {
      // stuff
    },
    todoAdded1f: (state, action) => {
      //stuff
    }
  }
})

const slice2 = createSlice({
  name: 'b',
  initialState: {},
  extraReducers: {
    [todoAdded2a]: (state, action) => {
      // stuff
    },
    [todoAdded2b](state, action) {
      // stuff
    },
    [todoAdded2c]: function (state, action) {
      // stuff
    }
  }
})
```

**Output** (<small>[basic.output.js](transforms\createSliceBuilder__testfixtures__\basic.output.js)</small>):

```js
const slice1 = createSlice({
  name: 'a',
  initialState: {},

  extraReducers: (builder) => {
    builder.addCase(todoAdded1a, (state, action) => {
      // stuff
    })

    builder.addCase(todoAdded1b, (state, action) => action.payload)

    builder.addCase(todoAdded1c + 'test', (state, action) => {
      // stuff
    })

    builder.addCase(todoAdded1d, (state, action) => {
      // stuff
    })

    builder.addCase(todoAdded1e, (state, action) => {
      // stuff
    })

    builder.addCase(todoAdded1f, (state, action) => {
      //stuff
    })
  }
})

const slice2 = createSlice({
  name: 'b',
  initialState: {},

  extraReducers: (builder) => {
    builder.addCase(todoAdded2a, (state, action) => {
      // stuff
    })

    builder.addCase(todoAdded2b, (state, action) => {
      // stuff
    })

    builder.addCase(todoAdded2c, (state, action) => {
      // stuff
    })
  }
})
```

---

### Overview (`packages/rtk-codemods/transforms/createSliceReducerBuilder/README.md`)

# createSliceReducerBuilder

Rewrites uses of Redux Toolkit's `createSlice` API to use the "builder callback" syntax for the `reducers` field, to make it easier to add prepared reducers and thunks inside of `createSlice`.

Note that unlike the `createReducerBuilder` and `createSliceBuilder` transforms (which both were fixes for deprecated/removed overloads), this is entirely optional. You do not _need_ to apply this to an entire codebase unless you specifically want to. Otherwise, feel free to apply to specific slice files as needed.

Should work with both JS and TS files.

## Usage

```
npx @reduxjs/rtk-codemods createSliceReducerBuilder path/of/files/ or/some**/*glob.js

# or

yarn global add @reduxjs/rtk-codemods
@reduxjs/rtk-codemods createSliceReducerBuilder path/of/files/ or/some**/*glob.js
```

## Local Usage

```
node ./bin/cli.mjs createSliceReducerBuilder path/of/files/ or/some**/*glob.js
```

## Input / Output

---

### Overview (`packages/toolkit/scripts/issue-triage/README.md`)

# GitHub Issues Triage Tool

A TypeScript-based tool for automatically triaging and categorizing GitHub issues in the Redux Toolkit repository.

## Features

- Fetches open issues from GitHub API
- Categorizes issues based on labels, titles, and content
- Generates markdown reports with statistics
- Caches data to avoid API rate limiting
- Supports both Node.js and Bun runtimes

## Setup

1. Install dependencies:

```bash
npm install
```

2. Build the TypeScript code:

```bash
npm run build
```

## Usage

Run the triage tool:

```bash
npm start
```

Or for development (builds and runs):

```bash
npm run dev
```

### Using Cached Data

The tool automatically saves fetched data to `cache/issues-data.json` after each run. To use cached data instead of fetching fresh data (useful for development and testing):

```bash
npm start -- --use-cache
```

Or with the dev command:

```bash
npm run dev -- --use-cache
```

**Note:** The `--use-cache` flag is primarily for development/testing purposes. It loads data from the cache file instantly without making API calls. If the cache file doesn't exist or is corrupted, the tool will automatically fetch fresh data.

## Project Structure

```
scripts/issue-triage/
├── src/           # TypeScript source files
├── dist/          # Compiled JavaScript output
├── cache/         # Cached JSON data from GitHub
├── reports/       # Generated triage reports
├── package.json   # Project dependencies
└── tsconfig.json  # TypeScript configuration
```

## Requirements

- Node.js 18+ or Bun 1.0+
- TypeScript 5.0+

## Development

This tool is built with TypeScript using ES modules (ESM). The configuration supports both Node.js and Bun runtimes for maximum flexibility.

---

### Introduction Getting Started (`docs/introduction/getting-started.md`)

---
id: getting-started
title: Getting Started
sidebar_label: Getting Started
hide_title: true
---

import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';

&nbsp;

# Getting Started with Redux Toolkit

## Purpose

The **Redux Toolkit** package is intended to be the standard way to write [Redux](/) logic. It was originally created to help address three common concerns about Redux:

- "Configuring a Redux store is too complicated"
- "I have to add a lot of packages to get Redux to do anything useful"
- "Redux requires too much boilerplate code"

We can't solve every use case, but in the spirit of [`create-react-app`](https://github.com/facebook/create-react-app), we can try to provide some tools that abstract over the setup process and handle the most common use cases, as well as include some useful utilities that will let the user simplify their application code.

Redux Toolkit also includes a powerful data fetching and caching capability that we've dubbed ["RTK Query"](#rtk-query). It's included in the package as a separate set of entry points. It's optional, but can eliminate the need to hand-write data fetching logic yourself.

**These tools should be beneficial to all Redux users**. Whether you're a brand new Redux user setting up your
first project, or an experienced user who wants to simplify an existing application, **Redux Toolkit** can help
you make your Redux code better.

## Installation

### Create a React Redux App

The recommended way to start new apps with React and Redux Toolkit is by using [our official Redux Toolkit + TS template for Vite](https://github.com/reduxjs/redux-templates), or by creating a new Next.js project using [Next's `with-redux` template](https://github.com/vercel/next.js/tree/canary/examples/with-redux).

Both of these already have Redux Toolkit and React-Redux configured appropriately for that build tool, and come with a small example app that demonstrates how to use several of Redux Toolkit's features.

```bash
# Vite with our Redux+TS template
# (using the `tiged` tool to clone and extract the template)
npx tiged reduxjs/redux-templates/packages/vite-template-redux my-app

# Next.js using the `with-redux` template
npx create-next-app --example with-redux my-app
```

For React Native, we have [an official Redux+TS template for Expo](https://github.com/reduxjs/redux-templates/tree/master/packages/expo-template-redux-typescript):

```bash
npx tiged reduxjs/redux-templates/packages/expo-template-redux-typescript my-app
```

### An Existing App

Redux Toolkit is available as a package on NPM for use with a module bundler or in a Node application:

<Tabs>
  <TabItem value="npm" label="npm" default>

```bash
npm install @reduxjs/toolkit
```

If you need React bindings:

```bash
npm install react-redux
```

  </TabItem>
  <TabItem value="yarn" label="yarn" default>

```bash
yarn add @reduxjs/toolkit
```

If you need React bindings:

```bash
yarn add react-redux
```

  </TabItem>
</Tabs>

The package includes a precompiled browser ESM build that can be loaded from a `<script type="module">` tag. That build imports its dependencies by package name, so using it without a bundler also requires an [import map](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script/type/importmap).

<details>
<summary>Example: loading Redux Toolkit in the browser without a bundler</summary>

```html
<script type="importmap">
  {
    "imports": {
      "@reduxjs/toolkit": "https://unpkg.com/@reduxjs/toolkit@2.12.0/dist/redux-toolkit.browser.mjs",
      "immer": "https://unpkg.com/immer@11.1.16/dist/immer.production.mjs",
      "redux": "https://unpkg.com/redux@5.0.1/dist/redux.mjs",
      "redux-thunk": "https://unpkg.com/redux-thunk@3.1.0/dist/redux-thunk.mjs",
      "reselect": "https://unpkg.com/reselect@5.2.0/dist/reselect.mjs"
    }
  }
</script>
<script type="module">
  import { configureStore, createSlice } from '@reduxjs/toolkit'

  const counterSlice = createSlice({
    name: 'counter',
    initialState: { value: 0 },
    reducers: {
      incremented: (state) => {
        state.value += 1
      },
    },
  })

  const store = configureStore({
    reducer: counterSlice.reducer,
  })

  store.dispatch(counterSlice.actions.incremented())
  console.log(store.getState())
</script>
```

Pin the dependency versions to match your installed packages. If CDN URLs aren't an option (for example, in a browser extension), copy the files into your project and point the import map at those local paths instead.

</details>

## Requirements

### TypeScript

Redux Toolkit follows [DefinitelyTyped's policy](https://github.com/DefinitelyTyped/DefinitelyTyped#support-window) of supporting TypeScript versions released within the past two years. As of RTK 2.11, this means we support:

| RTK Version | Minimum TypeScript |
| ----------- | ------------------ |
| 2.x         | 5.4+               |
| 1.9.x       | 4.7+               |

<details>
<summary>Using an older TypeScript version?</summary>

If you're unable to upgrade TypeScript, RTK may still work with older versions, but you may encounter type errors or missing type inference. We strongly recommend upgrading to take advantage of improved type safety and developer experience.

</details>

## What's Included

Redux Toolkit includes these APIs:

- [`configureStore()`](../api/configureStore.mdx): wraps `createStore` to provide simplified configuration options and good defaults. It can automatically combine your slice reducers, adds whatever Redux middleware you supply, includes `redux-thunk` by default, and enables use of the Redux DevTools Extension.
- [`createReducer()`](../api/createReducer.mdx): that lets you supply a lookup table of action types to case reducer functions, rather than writing switch statements. In addition, it automatically uses the [`immer` library](https://github.com/immerjs/immer) to let you write simpler immutable updates with normal mutative code, like `state.todos[3].completed = true`.
- [`createAction()`](../api/createAction.mdx): generates an action creator function for the given action type string.
- [`createSlice()`](../api/createSlice.mdx): accepts an object of reducer functions, a slice name, and an initial state value, and automatically generates a slice reducer with corresponding action creators and action types.
- [`combineSlices()`](../api/combineSlices.mdx): combines multiple slices into a single reducer, and allows "lazy loading" of slices after initialisation.
- [`createAsyncThunk`](../api/createAsyncThunk.mdx): accepts an action type string and a function that returns a promise, and generates a thunk that dispatches `pending/fulfilled/rejected` action types based on that promise
- [`createEntityAdapter`](../api/createEntityAdapter.mdx): generates a set of reusable reducers and selectors to manage normalized data in the store
- The [`createSelector` utility](../api/createSelector.mdx) from the [Reselect](https://github.com/reduxjs/reselect) library, re-exported for ease of use.

## RTK Query

[**RTK Query**](../rtk-query/overview.md) is provided as an optional addon within the `@reduxjs/toolkit` package. It is purpose-built to solve the use case of data fetching and caching, supplying a compact, but powerful toolset to define an API interface layer for your app. It is intended to simplify common cases for loading data in a web application, eliminating the need to hand-write data fetching & caching logic yourself.

RTK Query is built on top of the Redux Toolkit core for its implementation, using [Redux](/) internally for its architecture. Although knowledge of Redux and RTK are not required to use RTK Query, you should explore all of the additional global store management capabilities they provide, as well as installing the [Redux DevTools browser extension](https://github.com/reduxjs/redux-devtools), which works flawlessly with RTK Query to traverse and replay a timeline of your request & cache behavior.

RTK Query is included within the installation of the core Redux Toolkit package. It is available via either of the two entry points below:

```ts no-transpile
import { createApi } from '@reduxjs/toolkit/query'

/* React-specific entry point that automatically generates
   hooks corresponding to the defined endpoints */
import { createApi } from '@reduxjs/toolkit/query/react'
```

### What's included

RTK Query includes these APIs:

- [`createApi()`](../rtk-query/api/createApi.mdx): The core of RTK Query's functionality. It allows you to define a set of endpoints and describe how to retrieve data from a series of endpoints, including configuration of how to fetch and transform that data. In most cases, you should use this once per app, with "one API slice per base URL" as a rule of thumb.
- [`fetchBaseQuery()`](../rtk-query/api/fetchBaseQuery.mdx): A small wrapper around [`fetch`](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API) that aims to simplify requests. Intended as the recommended `baseQuery` to be used in `createApi` for the majority of users.
- [`<ApiProvider />`](../rtk-query/api/ApiProvider.mdx): Can be used as a `Provider` if you **do not already have a Redux store**.
- [`setupListeners()`](../rtk-query/api/setupListeners.mdx): A utility used to enable `refetchOnMount` and `refetchOnReconnect` behaviors.

See the [**RTK Query Overview**](../rtk-query/overview.md) page for more details on what RTK Query is, what problems it solves, and how to use it.

## Learn Redux

To learn how to use Redux Toolkit, start with the [**Redux Quick Start**](/tutorials/quick-start), then work through the [**Redux Essentials tutorial**](/tutorials/essentials/part-1-overview-concepts). The [**Tutorials Index**](/tutorials/index) lists all of the tutorials and video resources, and the [**Getting Started with Redux**](/introduction/getting-started) page has links for help and discussion.

---

### Rtk Query Overview (`docs/rtk-query/overview.md`)

---
id: overview
title: RTK Query Overview
sidebar_label: RTK Query Overview
hide_title: true
description: 'RTK Query > Overview: a summary of the RTK Query data caching API for Redux Toolkit'
---

&nbsp;

# RTK Query Overview

:::tip What You'll Learn

- What RTK Query is and what problems it solves
- What APIs are included in RTK Query
- Basic RTK Query usage

:::

**RTK Query** is a powerful data fetching and caching tool. It is designed to simplify common cases for loading data in a web application, **eliminating the need to hand-write data fetching & caching logic yourself**.

RTK Query is **an optional addon included in the Redux Toolkit package**, and its functionality is built on top of the other APIs in Redux Toolkit.

:::info

To learn how to use RTK Query, see the full ["Redux Essentials" tutorial](/tutorials/essentials/part-7-rtk-query-basics) on the Redux core docs site.

If you prefer a video course, you can [watch this RTK Query video course by Lenz Weber-Tronic, the creator of RTK Query, for free at Egghead](https://egghead.io/courses/rtk-query-basics-query-endpoints-data-flow-and-typescript-57ea3c43?af=7pnhj6) or take a look at the first lesson right here:

<div style={{position:"relative",paddingTop:"56.25%"}}>
  <iframe 
    src="https://app.egghead.io/lessons/redux-course-introduction-and-application-walk-through-for-rtk-query-basics/embed?af=7pnhj6" 
    title="RTK Query Video course at Egghead: Course Introduction and Application Walk through for RTK Query Basics"
    frameborder="0" 
    allowfullscreen
    style={{position:"absolute",top:0,left:0,width:"100%",height:"100%"}}
  ></iframe>
</div>

:::

## Motivation

Web applications normally need to fetch data from a server in order to display it. They also usually need to make updates to that data, send those updates to the server, and keep the cached data on the client in sync with the data on the server. This is made more complicated by the need to implement other behaviors used in today's applications:

- Tracking loading state in order to show UI spinners
- Avoiding duplicate requests for the same data
- Optimistic updates to make the UI feel faster
- Managing cache lifetimes as the user interacts with the UI

The Redux core has always been very minimal - it's up to developers to write all the actual logic. That means that Redux has never included anything built in to help solve these use cases. The Redux docs have taught [some common patterns for dispatching actions around the request lifecycle to track loading state and request results](/tutorials/fundamentals/part-7-standard-patterns#async-request-status), and [Redux Toolkit's `createAsyncThunk` API](../api/createAsyncThunk.mdx) was designed to abstract that typical pattern. However, users still have to write significant amounts of reducer logic to manage the loading state and the cached data.

Over the last couple years, the React community has come to realize that **"data fetching and caching" is really a different set of concerns than "state management"**. While you can use a state management library like Redux to cache data, the use cases are different enough that it's worth using tools that are purpose-built for the data fetching use case.

RTK Query takes inspiration from other tools that have pioneered solutions for data fetching, like Apollo Client, React Query, Urql, and SWR, but adds a unique approach to its API design:

- The data fetching and caching logic is built on top of Redux Toolkit's `createSlice` and `createAsyncThunk` APIs
- Because Redux Toolkit is UI-agnostic, RTK Query's functionality can be used with any UI layer
- API endpoints are defined ahead of time, including how to generate query parameters from arguments and transform responses for caching
- RTK Query can also generate React hooks that encapsulate the entire data fetching process, provide `data` and `isLoading` fields to components, and manage the lifetime of cached data as components mount and unmount
- RTK Query provides "cache entry lifecycle" options that enable use cases like streaming cache updates via websocket messages after fetching the initial data
- We have early working examples of code generation of API slices from OpenAPI and GraphQL schemas
- Finally, RTK Query is completely written in TypeScript, and is designed to provide an excellent TS usage experience

## What's included

### APIs

RTK Query is included within the installation of the core Redux Toolkit package. It is available via either of the two entry points below:

```ts no-transpile
import { createApi } from '@reduxjs/toolkit/query'

/* React-specific entry point that automatically generates
   hooks corresponding to the defined endpoints */
import { createApi } from '@reduxjs/toolkit/query/react'
```

RTK Query includes these APIs:

- [`createApi()`](./api/createApi.mdx): The core of RTK Query's functionality. It allows you to define a set of "endpoints" that describe how to retrieve data from backend APIs and other async sources, including the configuration of how to fetch and transform that data. In most cases, you should use this once per app, with "one API slice per base URL" as a rule of thumb.
- [`fetchBaseQuery()`](./api/fetchBaseQuery.mdx): A small wrapper around [`fetch`](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API) that aims to simplify requests. Intended as the recommended `baseQuery` to be used in `createApi` for the majority of users.
- [`<ApiProvider />`](./api/ApiProvider.mdx): Can be used as a `Provider` if you **do not already have a Redux store**.
- [`setupListeners()`](./api/setupListeners.mdx): A utility used to enable `refetchOnMount` and `refetchOnReconnect` behaviors.

### Bundle Size

RTK Query adds a fixed one-time amount to your app's bundle size. Since RTK Query builds on top of Redux Toolkit and React-Redux, the added size varies depending on whether you are already using those in your app. The estimated min+gzip bundle sizes are:

- If you are using RTK already: ~9kb for RTK Query and ~2kb for the hooks.
- If you are not using RTK already:
  - Without React: 17 kB for RTK+dependencies+RTK Query
  - With React: 19kB + React-Redux, which is a peer dependency

Adding additional endpoint definitions should only increase size based on the actual code inside the `endpoints` definitions, which will typically be just a few bytes.

The functionality included in RTK Query quickly pays for the added bundle size, and the elimination of hand-written data fetching logic should be a net improvement in size for most meaningful applications.

## Basic Usage

### Create an API Slice

RTK Query is included within the installation of the core Redux Toolkit package. It is available via either of the two entry points below:

```ts
import { createApi } from '@reduxjs/toolkit/query'

/* React-specific entry point that automatically generates
   hooks corresponding to the defined endpoints */
import { createApi } from '@reduxjs/toolkit/query/react'
```

For typical usage with React, start by importing `createApi` and defining an "API slice" that lists the server's base URL and which endpoints we want to interact with:

```ts
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'
import type { Pokemon } from './types'

// Define a service using a base URL and expected endpoints
export const pokemonApi = createApi({
  reducerPath: 'pokemonApi',
  baseQuery: fetchBaseQuery({ baseUrl: 'https://pokeapi.co/api/v2/' }),
  endpoints: (build) => ({
    getPokemonByName: build.query<Pokemon, string>({
      query: (name) => `pokemon/${name}`,
    }),
  }),
})

// Export hooks for usage in functional components, which are
// auto-generated based on the defined endpoints
export const { useGetPokemonByNameQuery } = pokemonApi
```

### Configure the Store

The "API slice" also contains an auto-generated Redux slice reducer and a custom middleware that manages subscription lifetimes. Both of those need to be added to the Redux store:

```ts
import { configureStore } from '@reduxjs/toolkit'
// Or from '@reduxjs/toolkit/query/react'
import { setupListeners } from '@reduxjs/toolkit/query'
import { pokemonApi } from './services/pokemon'

export const store = configureStore({
  reducer: {
    // Add the generated reducer as a specific top-level slice
    [pokemonApi.reducerPath]: pokemonApi.reducer,
  },
  // Adding the api middleware enables caching, invalidation, polling,
  // and other useful features of `rtk-query`.
  middleware: (getDefaultMiddleware) =>
    getDefaultMiddleware().concat(pokemonApi.middleware),
})

// optional, but required for refetchOnFocus/refetchOnReconnect behaviors
// see `setupListeners` docs - takes an optional callback as the 2nd arg for customization
setupListeners(store.dispatch)
```

### Use Hooks in Components

Finally, import the auto-generated React hooks from the API slice into your component file, and call the hooks in your component with any needed parameters. RTK Query will automatically fetch data on mount, re-fetch when parameters change, provide `{data, isFetching}` values in the result, and re-render the component as those values change:

```ts
import * as React from 'react'
import { useGetPokemonByNameQuery } from './services/pokemon'

export default function App() {
  // Using a query hook automatically fetches data and returns query values
  const { data, error, isLoading } = useGetPokemonByNameQuery('bulbasaur')
  // Individual hooks are also accessible under the generated endpoints:
  // const { data, error, isLoading } = pokemonApi.endpoints.getPokemonByName.useQuery('bulbasaur')

  // render UI based on data and loading state
}
```

## Further Information

See the [**RTK Query Quick Start tutorial**](../tutorials/rtk-query.mdx) for examples of how to add RTK Query to a project that uses Redux Toolkit, set up an "API slice" with endpoint definitions, and how to use the auto-generated React hooks in your components.

The [**RTK Query usage guide section**](./usage/queries.mdx) has information on topics like [querying data](./usage/queries.mdx), [using mutations to send updates to the server](./usage/mutations.mdx), [streaming cache updates](./usage/streaming-updates.mdx), and much more.

The [**Examples page**](./usage/examples.mdx) has runnable CodeSandboxes that demonstrate topics like [making queries with GraphQL](./usage/examples.mdx#react-with-graphql), [authentication](./usage/examples.mdx#authentication), and even [using RTK Query with other UI libraries like Svelte](./usage/examples.mdx#svelte).

---

### Rtk Query Internal Overview (`docs/rtk-query/internal/overview.mdx`)

# RTKQ internal

## Overview

> RTK Query is a powerful data fetching and caching tool built on top of Redux Toolkit. It is designed to simplify the process of fetching, caching, and updating server state in your application. It is built on top of Redux Toolkit and uses Redux internally.

This documentation is intended to provide a high-level overview of the internal architecture of RTK-Query. It is not intended to be a comprehensive guide to the library, but rather a guide to the internal architecture and how it works.

## createApi - The Entry Point

When `createApi()` is called it takes the options provided and calls internally the `buildCreateApi()` function passing into it two modules:

_Modules are RTK-Query's method of customizing how the `createApi` method handles endpoints._

- `coreModule()` - responsible for the majority of the internal handling using core redux logic i.e. slices, reducers, asyncThunks.
- `reactHooksModule()` - a module that generates react hooks from endpoints using react-redux

## Core Module

The core module takes the `api` and the options passed to `createApi()`. In turn an internal set of "build" methods are called. Each of these build methods create a set of functions which are assigned to either `api.util` or `api.internalActions` and/or passed to a future "build" step.

### buildThunks

RTK-Query's internal functionality operates using the same `asyncThunk` exposed from RTK. In the first "build" method, a number of thunks are generated for the core module to use:

- `queryThunk`
- `mutationThunk`
- `patchedQueryData`
- `updateQueryData`
- `upsertQueryData`
- `prefetch`
- `buildMatchThunkActions`

### buildSlice

RTK-Query uses a very familiar redux-centric architecture. Where the `api` is a slice of your store, the `api` has its own slices created within it. These slices are where the majority of the RTKQ magic happens.

The slices built inside this "build" are:
_Some of which have their own actions_

- `querySlice`
- `mutationSlice`
- `invalidationSlice`
- `subscriptionSlice` (used as a dummy slice to generate actions internally)
- `internalSubscriptionsSlice`
- `configSlice` (internal tracking of focus state, online state, hydration etc)

buildSlice also exposes the core action `resetApiState` which is subsequently added to the `api.util`

### buildMiddleware

RTK-Query has a series of custom middlewares established within its store to handle additional responses in addition to the core logic established within the slices from buildSlice.

Each middleware built during this step is referred to internally as a "Handler" and are as follows:

- `buildDevCheckHandler`
- `buildCacheCollectionHandler`
- `buildInvalidationByTagsHandler`
- `buildPollingHandler`
- `buildCacheLifecycleHandler`
- `buildQueryLifecycleHandler`

### buildSelectors

build selectors is a crucial step that exposes to the `api` and utils:

- `buildQuerySelector`
- `buildMutationSelector`
- `selectInvalidatedBy`
- `selectCachedArgsForQuery`

### return

Finally each endpoint passed into the `createApi()` is iterated over and assigned either the query or the mutation selectors, initiators and match cases.

---

### Rtk Query Api Created Api Overview (`docs/rtk-query/api/created-api/overview.mdx`)

---
id: overview
title: Overview
sidebar_label: API Slice Overview
hide_title: true
---

&nbsp;

# Generated API Slices

## API Slice Overview

When you call [`createApi`](../createApi.mdx), it automatically generates and returns an API service "slice" object structure containing Redux logic you can use to interact with the endpoints you defined. This slice object includes a reducer to manage cached data, a middleware to manage cache lifetimes and subscriptions, and selectors and thunks for each endpoint. If you imported `createApi` from the React-specific entry point, it also includes auto-generated React hooks for use in your components.

This section documents the contents of that API structure, with the different fields grouped by category. The API types and descriptions are listed on separate pages for each category.

:::tip

Typically, you should only have one API slice per base URL that your application needs to communicate with. For example, if your site fetches data from both `/api/posts` and `/api/users`, you would have a single API slice with `/api/` as the base URL, and separate endpoint definitions for `posts` and `users`. This allows you to effectively take advantage of [automated re-fetching](../../usage/automated-refetching.mdx) by defining [tag](../../usage/automated-refetching.mdx#tags) relationships across endpoints.

This is because:

- Automatic tag invalidation only works within a single API slice. If you have multiple API slices, the automatic invalidation won't work across them.
- Every `createApi` call generates its own middleware, and each middleware added to the store will run checks against every dispatched action. That has a perf cost that adds up. So, if you called `createApi` 10 times and added 10 separate API middleware to the store, that will be noticeably slower perf-wise.

For maintainability purposes, you may wish to split up endpoint definitions across multiple files, while still maintaining a single API slice which includes all of these endpoints. See [code splitting](../../usage/code-splitting.mdx) for how you can use the `injectEndpoints` property to inject API endpoints from other files into a single API slice definition.

:::

```
/* Detailed source-code truncated for AI context efficiency. */
```

## Redux Integration

Internally, `createApi` will call [the Redux Toolkit `createSlice` API](https://redux-toolkit.js.org/api/createSlice) to generate a slice reducer and corresponding action creators with the appropriate logic for caching fetched data. It also automatically generates a custom Redux middleware that manages subscription counts and cache lifetimes.

The generated slice reducer and the middleware both need to be adding to your Redux store setup in `configureStore` in order to work correctly.

:::info API Reference

- [API Slices: Redux Integration](./redux-integration.mdx)

:::

## Endpoints

The API slice object will have an `endpoints` field inside. This section maps the endpoint names you provided to `createApi` to the core Redux logic (thunks and selectors) used to trigger data fetches and read cached data for that endpoint. If you're using the React-specific version of `createApi`, each endpoint definition will also contain the auto-generated React hooks for that endpoint.

:::info API Reference

- [API Slices: Endpoints](./endpoints.mdx)

:::

## Code Splitting and Generation

Each API slice allows [additional endpoint definitions to be injected at runtime](../../usage/code-splitting.mdx) after the initial API slice has been defined. This can be beneficial for apps that may have _many_ endpoints.

The individual API slice endpoint definitions can also be split across multiple files. This is primarily useful for working with API slices that were [code-generated from an API schema file](../../usage/code-generation.mdx), allowing you to add additional custom behavior and configuration to a set of automatically-generated endpoint definitions.

Each API slice object has `injectEndpoints` and `enhanceEndpoints` functions to support these use cases.

:::info API Reference

- [API Slices: Code Splitting and Generation](./code-splitting.mdx)

:::

## API Slice Utilities

The `util` field includes various utility functions that can be used to manage the cache, including
manually updating query cache data, triggering pre-fetching of data, manually invalidating tags,
and manually resetting the api state, as well as other utility functions that can be used in
various scenarios, including SSR.

:::info API Reference

- [API Slices: Utilities](./api-slice-utils.mdx)

:::

## Internal Actions

The `internalActions` field contains a set of additional thunks that are used for internal behavior, such as managing updates based on focus.

## React Hooks

The core RTK Query `createApi` method is UI-agnostic, in the same way that the Redux core library and Redux Toolkit are UI-agnostic. They are all plain JS logic that can be used anywhere.

However, RTK Query also provides the ability to auto-generate React hooks for each of your endpoints. Since this specifically depends on React itself, RTK Query provides an alternate entry point that exposes a customized version of `createApi` that includes that functionality:

```js
import { createApi } from '@reduxjs/toolkit/query/react'
```

If you have used the React-specific version of `createApi`, the generated `Api` slice structure will also contain a set of React hooks. These endpoint hooks are available as `api.endpoints[endpointName].useQuery` or `api.endpoints[endpointName].useMutation`, matching how you defined that endpoint.

The same hooks are also added to the `Api` object itself, and given auto-generated names based on the endpoint name and query/mutation type.

For example, if you had endpoints for `getPosts` and `updatePost`, these options would be available:

```ts title="Generated React Hook names" no-transpile
// Hooks attached to the endpoint definition
const { data } = api.endpoints.getPosts.useQuery()
const { data } = api.endpoints.updatePost.useMutation()

// Same hooks, but given unique names and attached to the API slice object
const { data } = api.useGetPostsQuery()
const [updatePost] = api.useUpdatePostMutation()
```

The React-specific version of `createApi` also generates a `usePrefetch` hook, attached to the `Api` object, which can be used to initiate fetching data ahead of time.

:::info API Reference

- [API Slices: React Hooks](./hooks.mdx)

:::

---

### Package.Json (`docs/package.json`)

{
  "name": "docs",
  "devDependencies": {
    "@manaflair/redux-batch": "^1.0.0",
    "@types/nanoid": "^2.1.0",
    "@types/react": "^19.0.1",
    "async-mutex": "^0.3.2",
    "axios": "^0.20.0",
    "formik": "^2.1.5",
    "graphql-request": "^3.4.0",
    "immutable": "^3.8.2",
    "nanoid": "^3.1.23",
    "next-redux-wrapper": "^7.0.5",
    "redux-persist": "^6.0.0",
    "rxjs": "^6.6.2"
  }
}

---

### Tsconfig.Json (`docs/tsconfig.json`)

{
  "compilerOptions": {
    "allowSyntheticDefaultImports": true,
    "esModuleInterop": true,
    "ignoreDeprecations": "6.0",
    "moduleResolution": "bundler",
    "noUncheckedSideEffectImports": false,
    "noUnusedLocals": false,
    "noUnusedParameters": false,
    "allowUnusedLabels": true,
    "skipLibCheck": true,
    "noEmitOnError": false,
    "strict": true,
    "removeComments": false,
    "module": "ES2020",
    "target": "ES2020",
    "allowJs": true,
    "checkJs": true,
    "jsx": "preserve",
    "traceResolution": false,
    "explainFiles": false,
    "paths": {
      "react": ["../docs/node_modules/@types/react"],
      "react-dom": ["../docs/node_modules/@types/react-dom"],
      "@reduxjs/toolkit": ["../packages/toolkit/dist/index.d.ts"],
      "@reduxjs/toolkit/query": ["../packages/toolkit/dist/query/index.d.ts"],
      "@reduxjs/toolkit/query/react": [
        "../packages/toolkit/dist/query/react/index.d.ts"
      ],
      "@virtual/*": ["../docs/virtual/*"],
      "your-cool-library": ["../docs/virtual/your-cool-library/index.ts"],
      "redux-logger": ["../docs/virtual/redux-logger/index.ts"],
      "petstore-api.generated": [
        "../docs/virtual/petstore-api.generated/index.ts"
      ]
    }
  }
}

---

### Api ActionCreatorMiddleware (`docs/api/actionCreatorMiddleware.mdx`)

---
id: actionCreatorMiddleware
title: Action Creator Middleware
sidebar_label: Action Creator Middleware
hide_title: true
---

&nbsp;

# Action Creator Middleware

A custom middleware that detects if an action creator has been mistakenly dispatched, instead of being called before dispatching.

A common mistake is to call `dispatch(actionCreator)` instead of `dispatch(actionCreator())`.
This tends to "work" as the action creator has the static `type` property, but can lead to unexpected behavior.

## Options

```ts no-transpile
export interface ActionCreatorInvariantMiddlewareOptions {
  /**
   * The function to identify whether a value is an action creator.
   * The default checks for a function with a static type property and match method.
   */
  isActionCreator?: (action: unknown) => action is Function & { type?: unknown }
}
```

## Exports

### `createActionCreatorInvariantMiddleware`

Creates an instance of the action creator check middleware, with the given options.

You will most likely not need to call this yourself, as `getDefaultMiddleware` already does so.
Example:

```ts
// file: reducer.ts noEmit

export default function (state = {}, action: any) {
  return state
}

// file: store.ts

import {
  configureStore,
  createActionCreatorInvariantMiddleware,
  Tuple,
} from '@reduxjs/toolkit'
import reducer from './reducer'

// Augment middleware to consider all functions with a static type property to be action creators
const isActionCreator = (
  action: unknown,
): action is Function & { type: unknown } =>
  typeof action === 'function' && 'type' in action

const actionCreatorMiddleware = createActionCreatorInvariantMiddleware({
  isActionCreator,
})

const store = configureStore({
  reducer,
  middleware: () => new Tuple(actionCreatorMiddleware),
})
```

---

### Api AutoBatchEnhancer (`docs/api/autoBatchEnhancer.mdx`)

---
id: autoBatchEnhancer
title: autoBatchEnhancer
sidebar_label: autoBatchEnhancer
hide_title: true
---

&nbsp;

# `autoBatchEnhancer`

A Redux store enhancer that looks for one or more "low-priority" dispatched actions in a row, and queues a callback to run subscriber notifications on a delay. It then notifies subscribers either when the queued callback runs, or when the next "normal-priority" action is dispatched, whichever is first.

## Basic Usage

```ts
import {
  createSlice,
  configureStore,
  autoBatchEnhancer,
  prepareAutoBatched,
} from '@reduxjs/toolkit'

interface CounterState {
  value: number
}

const counterSlice = createSlice({
  name: 'counter',
  initialState: { value: 0 } satisfies CounterState as CounterState,
  reducers: {
    incrementBatched: {
      // Batched, low-priority
      reducer(state) {
        state.value += 1
      },
      // highlight-start
      // Use the `prepareAutoBatched` utility to automatically
      // add the `action.meta[SHOULD_AUTOBATCH]` field the enhancer needs
      prepare: prepareAutoBatched<void>(),
      // highlight-end
    },
    // Not batched, normal priority
    decrementUnbatched(state) {
      state.value -= 1
    },
  },
})
const { incrementBatched, decrementUnbatched } = counterSlice.actions

// includes batch enhancer by default, as of RTK 2.0
const store = configureStore({
  reducer: counterSlice.reducer,
})
```

## API

### `autoBatchEnhancer`

```ts title="autoBatchEnhancer signature" no-transpile
export type SHOULD_AUTOBATCH = string
type AutoBatchOptions =
  | { type: 'tick' }
  | { type: 'timer'; timeout: number }
  | { type: 'raf' }
  | { type: 'callback'; queueNotification: (notify: () => void) => void }

export type autoBatchEnhancer = (options?: AutoBatchOptions) => StoreEnhancer
```

:::tip
As of RTK 2.0, the `autoBatchEnhancer` is included by default when calling `configureStore`.

This means to configure it, you should instead pass an callback that receives `getDefaultEnhancers` and calls it with your desired settings.

```ts title="Configuring autoBatchEnhancer with getDefaultEnhancers"
import { configureStore } from '@reduxjs/toolkit'

const store = configureStore({
  reducer: () => 0,
  enhancers: (getDefaultEnhancers) =>
    getDefaultEnhancers({
      autoBatch: { type: 'tick' },
    }),
})
```

:::

Creates a new instance of the autobatch store enhancer.

Any action that is tagged with `action.meta[SHOULD_AUTOBATCH] = true` will be treated as "low-priority", and a notification callback will be queued. The enhancer will delay notifying subscribers until either:

- The queued callback runs and triggers the notifications
- A "normal-priority" action (any action _without_ `action.meta[SHOULD_AUTOBATCH] = true`) is dispatched in the same tick

`autoBatchEnhancer` accepts options to configure how the notification callback is queued:

- `{type: 'raf'}`: queues using `requestAnimationFrame` (default)
- `{type: 'tick'}`: queues using `queueMicrotask`
- `{type: 'timer', timeout: number}`: queues using `setTimeout`
- `{type: 'callback', queueNotification: (notify: () => void) => void}`: lets you provide your own callback, such as a debounced or throttled function

The default behavior is to queue the notifications using `requestAnimationFrame`.

The `SHOULD_AUTOBATCH` value is meant to be opaque - it's currently a string for simplicity, but could be a `Symbol` in the future.

### `prepareAutoBatched`

```ts title="prepareAutoBatched signature" no-transpile
type prepareAutoBatched = <T>() => (payload: T) => { payload: T; meta: unknown }
```

Creates a function that accepts a `payload` value, and returns an object with `{payload, meta: {[SHOULD_AUTOBATCH]: true}}`. This is meant to be used with RTK's `createSlice` and its "`prepare` callback" syntax:

```ts no-transpile
createSlice({
  name: 'todos',
  initialState,
  reducers: {
    todoAdded: {
      reducer(state, action: PayloadAction<Todo>) {
        state.push(action.payload)
      },
      // highlight-start
      prepare: prepareAutoBatched<Todo>(),
      // highlight-end
    },
  },
})
```

## Batching Approach and Background

The post [A Comparison of Redux Batching Techniques](https://blog.isquaredsoftware.com/2020/01/blogged-answers-redux-batching-techniques/) describes four different approaches for "batching Redux actions/dispatches"

- a higher-order reducer that accepts multiple actions nested inside one real action, and iterates over them together
- an enhancer that wraps `dispatch` and debounces the notification callback
- an enhancer that wraps `dispatch` to accept an array of actions
- React's `unstable_batchedUpdates()`, which just combines multiple queued renders into one but doesn't affect subscriber notifications

This enhancer is a variation of the "debounce" approach, but with a twist.

Instead of _just_ debouncing _all_ subscriber notifications, it watches for any actions with a specific `action.meta[SHOULD_AUTOBATCH]: true` field attached.

When it sees an action with that field, it queues a callback. The reducer is updated immediately, but the enhancer does _not_ notify subscribers right way. If other actions with the same field are dispatched in succession, the enhancer will continue to _not_ notify subscribers. Then, when the queued callback runs, it finally notifies all subscribers, similar to how React batches re-renders.

The additional twist is also inspired by React's separation of updates into "low-priority" and "immediate" behavior (such as a render queued by an AJAX request vs a render queued by a user input that should be handled synchronously).

If some low-pri actions have been dispatched and a notification microtask is queued, then a _normal_ priority action (without the field) is dispatched, the enhancer will go ahead and notify all subscribers synchronously as usual, and _not_ notify them at the end of the tick.

This allows Redux users to selectively tag certain actions for effective batching behavior, making this purely opt-in on a per-action basis, while retaining normal notification behavior for all other actions.

### RTK Query and Batching

RTK Query already marks several of its key internal action types as batchable. By adding the `autoBatchEnhancer` to the store setup, it improves the overall UI performance, especially when rendering large lists of components that use the RTKQ query hooks.

---

### Api Codemods (`docs/api/codemods.mdx`)

---
id: codemods
title: Codemods
sidebar_label: Codemods
hide_title: true
---

&nbsp;

# Codemods

Per [the description in `1.9.0`](https://github.com/reduxjs/redux-toolkit/releases/tag/v1.9.0), we have removed the "object" argument from `createReducer` and `createSlice.extraReducers` in the RTK 2.0 major version. We've also added a new optional form of `createSlice.reducers` that uses a callback instead of an object.

To simplify upgrading codebases, we've published a set of codemods that will automatically transform the deprecated "object" syntax into the equivalent "builder" syntax.

The codemods package is available on NPM as [**`@reduxjs/rtk-codemods`**](https://www.npmjs.com/package/@reduxjs/rtk-codemods). It currently contains these codemods:

- `createReducerBuilder`: migrates `createReducer` calls that use the removed object syntax to the builder callback syntax
- `createSliceBuilder`: migrates `createSlice` calls that use the removed object syntax for `extraReducers` to the builder callback syntax
- `createSliceReducerBuilder`: migrates `createSlice` calls that use the still-standard object syntax for `reducers` to the optional new builder callback syntax, including uses of prepared reducers

To run the codemods against your codebase, run `npx @reduxjs/rtk-codemods <TRANSFORM NAME> path/of/files/ or/some**/*glob.js`.

Examples:

```bash
npx @reduxjs/rtk-codemods createReducerBuilder ./src

npx @reduxjs/rtk-codemods createSliceBuilder ./packages/my-app/**/*.ts
```

We also recommend re-running Prettier on the codebase before committing the changes.

**These codemods _should_ work, but we would greatly appreciate testing and feedback on more real-world codebases!**

Before:

```js
createReducer(initialState, {
  [todoAdded1a]: (state, action) => {
    // stuff
  },
  [todoAdded1b]: (state, action) => action.payload,
})

const slice1 = createSlice({
  name: 'a',
  initialState: {},
  extraReducers: {
    [todoAdded1a]: (state, action) => {
      // stuff
    },
    [todoAdded1b]: (state, action) => action.payload,
  },
})
```

After:

```js
createReducer(initialState, (builder) => {
  builder.addCase(todoAdded1a, (state, action) => {
    // stuff
  })

  builder.addCase(todoAdded1b, (state, action) => action.payload)
})

const slice1 = createSlice({
  name: 'a',
  initialState: {},

  extraReducers: (builder) => {
    builder.addCase(todoAdded1a, (state, action) => {
      // stuff
    })

    builder.addCase(todoAdded1b, (state, action) => action.payload)
  },
})
```

---

### Api CombineSlices (`docs/api/combineSlices.mdx`)

---
id: combineSlices
title: combineSlices
sidebar_label: combineSlices
hide_title: true
---

&nbsp;

# `combineSlices`

## Overview

A function that combines slices into a single reducer, and enables injection of more reducers after initialisation.

```ts
// file: slices/api.ts noEmit
import type { Api } from '@reduxjs/toolkit/query'

export declare const api: Api<() => any, {}, 'api', never>

// file: slices/users.ts noEmit
import type { Slice } from '@reduxjs/toolkit'

export declare const userSlice: Slice<string, {}, 'user'>

// file: slices/index.ts
import { combineSlices } from '@reduxjs/toolkit'
import { api } from './api'
import { userSlice } from './users'

export const rootReducer = combineSlices(api, userSlice)

// file: store.ts
import { configureStore } from '@reduxjs/toolkit'
import { rootReducer } from './slices'

export const store = configureStore({
  reducer: rootReducer,
})
```

:::note

A "slice" for `combineSlices` is typically created with [`createSlice`](./createSlice.mdx),
but can be any "slice-like" object with `reducerPath` and `reducer` properties (meaning RTK Query [API instances](/rtk-query/api/created-api/overview.mdx) are also compatible).

```ts no-transpile
const withUserReducer = rootReducer.inject({
  reducerPath: 'user',
  reducer: userReducer,
})

const withApiReducer = rootReducer.inject(fooApi)
```

For simplicity, this `{ reducerPath, reducer }` shape will be described in these docs as a "slice".

:::

## Parameters

`combineSlices` accepts a set of slices and/or reducer map objects, and combines them into a single reducer.

Slices will be mounted at their `reducerPath`, and items from reducer map objects will be mounted under their respective key.

```ts no-transpile
const rootReducer = combineSlices(counterSlice, baseApi, {
  user: userSlice.reducer,
  auth: authSlice.reducer,
})
// is like
const rootReducer = combineReducers({
  [counterSlice.reducerPath]: counterSlice.reducer,
  [baseApi.reducerPath]: baseApi.reducer,
  user: userSlice.reducer,
  auth: authSlice.reducer,
})
```

:::caution

If multiple slices/map objects have the same reducer path, the reducer provided later in the arguments will override the previous.

However, typing will not be able to account for this. It's best to ensure that all of your reducers will aim for a unique location.

:::

## Return Value

`combineSlices` returns a reducer function, with attached methods.

```ts no-transpile
interface CombinedSliceReducer<InitialState, DeclaredState = InitialState>
  extends Reducer<DeclaredState, AnyAction, Partial<DeclaredState>> {
  withLazyLoadedSlices<LazyLoadedSlices>(): CombinedSliceReducer<
    InitialState,
    DeclaredState & Partial<LazyLoadedSlices>
  >
  inject<Slice extends SliceLike>(
    slice: Slice,
    config?: InjectConfig
  ): CombinedSliceReducer<InitialState, DeclaredState & WithSlice<Slice>>
  selector: {
    (selectorFn: Selector, selectState?: SelectFromRootState) => WrappedSelector
    original(state: DeclaredState) => InitialState & Partial<DeclaredState>
  }
}
```

### `withLazyLoadedSlices`

It's recommended to [infer your RootState type from your store](/usage/usage-with-typescript#define-root-state-and-dispatch-types), which is inferred from the reducer. However, this can present issues if slices are lazy loaded, and thus not able to be inferred from.

`withLazyLoadedSlices` allows you to declare slices that will be added to state later, which will be included in the final state type.

One possible pattern of managing this would be with declaration merging:

```ts no-transpile title="Using declaration merging to declare injected slices"
// file: slices/index.ts
import { combineSlices } from '@reduxjs/toolkit'
import { staticSlice } from './static'

export interface LazyLoadedSlices {}

export const rootReducer =
  combineSlices(staticSlice).withLazyLoadedSlices<LazyLoadedSlices>()

// keys in LazyLoadedSlices are marked as optional
export type RootState = ReturnType<typeof rootReducer>

// file: slices/lazySlice.ts
import type { WithSlice } from '@reduxjs/toolkit'
import { rootReducer } from '.'

const lazySlice = createSlice({/* ... */})

declare module '.' {
  export interface LazyLoadedSlices extends WithSlice<typeof lazySlice> {}
}

const injectedReducer = rootReducer.inject(lazySlice)

// and/or

const injectedSlice = lazySlice.injectInto(rootReducer)
```

:::tip

The above example uses the `WithSlice` utility type for a slice mounted under its `reducerPath`. If the slice is mounted under a different key, you can declare it as a regular key instead.

```ts no-transpile title="Declaring a slice mounted outside its reducerPath"
// file: slices/lazySlice.ts
import { rootReducer } from '.'

const lazySlice = createSlice({/* ... */})

declare module '.' {
  export interface LazyLoadedSlices {
    customKey: LazyState
  }
}

const injectedReducer = rootReducer.inject({
  reducerPath: 'customKey',
  reducer: lazySlice.reducer,
})

// and/or

const injectedSlice = lazySlice.injectInto(rootReducer, {
  reducerPath: 'customKey',
})
```

:::

### `inject`

`inject` allows you to add a slice to your set of reducers after initialisation.
It expects to be passed a slice and an optional config, and returns an updated version of the reducer with the slice included.

This is mainly useful for lazy loading reducers.

```ts no-transpile
const reducerWithUser = rootReducer.inject(userSlice)
```

:::note

`inject` adds the slice to the map of reducers in your original reducer, but doesn't dispatch an action.

This means that the added reducer state will not show up in your store until the next action is dispatched.

:::

#### Reducer replacement

By default, replacing a reducer is not allowed.
In development mode, a warning will be logged to console if a new reducer instance is attempted to inject into a `reducerPath` that's already injected. (It won't warn if the same reducer instance is injected into the same place twice.)

If you wish to allow replacing a reducer with a new instance, you must explicitly pass `overrideExisting: true` as part of your configuration object.

```ts no-transpile
const reducerWithUser = rootReducer.inject(userSlice, {
  overrideExisting: true,
})
```

This may be useful for hot reload, or "removing" a reducer by replacing it with a function that always returns `null`.
Note that for predictable behavior, your types should account for all of the possible reducers you intend to occupy a path.

```ts no-transpile title="'Removing' a reducer, by replacing it with a no-op function"
declare module '.' {
  export interface LazyLoadedSlices {
    removable: RemovableState | null
  }
}

const withInjected = rootReducer.inject(
  { reducerPath: 'removable', reducer: removableReducer },
  { overrideExisting: true },
)

const emptyReducer = () => null

const removeReducer = () =>
  rootReducer.inject(
    { reducerPath: 'removable', reducer: emptyReducer },
    { overrideExisting: true },
  )
```

### `selector`

As noted previously, an injected reducer can still be undefined in state if no action has been dispatched.

Dealing with this possibly-optional state can be inconvenient when writing selectors, as you may end up with a lot of results being possibly undefined or relying on explicit defaults.

`selector` allows you to get around this, by wrapping the reducer state in a `Proxy` that ensures that any currently injected reducers evaluate to their initial state if they're currently `undefined` in state.

```ts no-transpile
declare module '.' {
  export interface LazyLoadedSlices extends WithSlice<typeof counterSlice> {}
}

const counterSlice = createSlice({
  name: 'counter',
  initialState: { value: 0 },
  reducers: {/* ... */},
})

const withCounter = rootReducer.inject(counterSlice)

const selectCounterValue = (rootState: RootState) => rootState.counter?.value // number | undefined

const wrappedSelectCounterValue = withCounter.selector(
  (rootState) => rootState.counter.value, // number
)

console.log(
  selectCounterValue({}), // undefined
  selectCounterValue({ counter: { value: 2 } }), // 2
  wrappedSelectCounterValue({}), // 0
  wrappedSelectCounterValue({ counter: { value: 2 } }), // 2
)
```

:::caution

The `Proxy` retrieves a reducer's initial state by calling it with a randomly generated action type - don't try to handle this as a special case inside your reducer.

:::

#### Nested combined reducer

The wrapped selector expects to use the state returned by the combined reducer as its first argument.

If the combined reducer is nested further inside the store state, pass a `selectState` callback as the second argument to `selector`:

```ts no-transpile
interface RootState {
  innerCombined: ReturnType<typeof combinedReducer>
}

const selectCounterValue = withCounter.selector(
  (combinedState) => combinedState.counter.value,
  (rootState: RootState) => rootState.innerCombined,
)

console.log(
  selectCounterValue({
    innerCombined: {},
  }), // 0
  selectCounterValue({
    innerCombined: {
      counter: {
        value: 2,
      },
    },
  }), // 2
)
```

#### `original`

Similar to [Immer usage](/usage/immer-reducers.md#debugging-and-inspecting-drafted-state), an `original` function is provided to retrieve the original state value provided to the `Proxy`.

This is mainly useful for debugging/inspecting, as `Proxy` instances tend to be displayed in a format that's hard to read.

The function is attached as a method on the `selector` function:

```ts no-transpile
const wrappedSelectCounterValue = withCounter.selector((rootState) => {
  console.log(withCounter.selector.original(rootState))
  return rootState.counter.value
})
```

## Slice integration

### `injectInto`

Slice instances returned by [`createSlice`](./createSlice) have an attached `injectInto` method, which receive an injectable reducer from `combineSlices` and returns an "injected" version of that slice.

```ts no-transpile
const injectedCounterSlice = counterSlice.injectInto(rootReducer)
```

An optional configuration object can be passed. This follows [`inject`](#inject)'s options with an additional `reducerPath` field, for injecting the slice under a path other than its current `reducerPath` property.

```ts no-transpile
const aCounterSlice = counterSlice.injectInto(rootReducer, {
  reducerPath: 'aCounter',
})
```

### `selectors` / `getSelectors`

Similar to [`selector`](#selector), the selectors from an "injected" slice instance behave slightly differently.

If the slice state is undefined in the store state passed, the selector will instead be called with the slice's initial state.

`selectors` will also reflect the change in `reducerPath` if one was made during injection.

```ts no-transpile
console.log(
  injectedCounterSlice.selectors.selectValue({}), // 0
  injectedCounterSlice.selectors.selectValue({ counter: { value: 2 } }), // 2
  aCounterSlice.selectors.selectValue({ aCounter: { value: 2 } }), // 2
)
```

---

### Api ConfigureStore (`docs/api/configureStore.mdx`)

---
id: configureStore
title: configureStore
sidebar_label: configureStore
hide_title: true
---

&nbsp;

# `configureStore`

The standard method for creating a Redux store. It uses the low-level Redux core `createStore` method internally, but wraps that to provide good defaults to the store setup for a better development experience.

## Purpose and Behavior

A standard Redux store setup typically requires multiple pieces of configuration:

- Combining the slice reducers into the root reducer
- Creating the middleware enhancer, usually with the thunk middleware or other side effects middleware, as well as middleware that might be used for development checks
- Adding the Redux DevTools enhancer, and composing the enhancers together
- Calling `createStore`

Legacy Redux usage patterns typically required several dozen lines of copy-pasted boilerplate to achieve this.

Redux Toolkit's `configureStore` simplifies that setup process, by doing all that work for you. One call to `configureStore` will:

- Call `combineReducers` to combine your slices reducers into the root reducer function
- Add the thunk middleware and called `applyMiddleware`
- In development, automatically add more middleware to check for common mistakes like accidentally mutating the state
- Automatically set up the Redux DevTools Extension connection
- Call `createStore` to create a Redux store using that root reducer and those configuration options

`configureStore` also offers an improved API and usage patterns compared to the original `createStore` by accepting named fields for `reducer`, `preloadedState`, `middleware`, `enhancers`, and `devtools`, as well as much better TS type inference.

## Parameters

`configureStore` accepts a single configuration object parameter, with the following options:

```
/* Detailed source-code truncated for AI context efficiency. */
```

### `reducer`

If this is a single function, it will be directly used as the root reducer for the store.

If it is an object of slice reducers, like `{users : usersReducer, posts : postsReducer}`,
`configureStore` will automatically create the root reducer by passing this object to the
[Redux `combineReducers` utility](/api/combinereducers).

### `middleware`

A callback which will receive `getDefaultMiddleware` as its argument,
and should return a middleware array.

If this option is provided, it should return all the middleware functions you
want added to the store. `configureStore` will automatically pass those to `applyMiddleware`.

If not provided, `configureStore` will call `getDefaultMiddleware` and use the
array of middleware functions it returns.

For more details on how the `middleware` parameter works and the list of middleware that are added by default, see the
[`getDefaultMiddleware` docs page](./getDefaultMiddleware.mdx).

:::note Tuple
TypeScript users are required to use a `Tuple` instance (if not using a `getDefaultMiddleware` result, which is already a `Tuple`), for better inference.

```ts no-transpile
import { configureStore, Tuple } from '@reduxjs/toolkit'

configureStore({
  reducer: rootReducer,
  middleware: () => new Tuple(additionalMiddleware, logger),
})
```

Javascript-only users are free to use a plain array if preferred.

:::

### `devTools`

If this is a boolean, it will be used to indicate whether `configureStore` should automatically enable support for [the Redux DevTools browser extension](https://github.com/reduxjs/redux-devtools).

If it is an object, then the DevTools Extension will be enabled, and the options object will be passed to `composeWithDevtools()`. See
the DevTools Extension docs for [`EnhancerOptions`](https://github.com/reduxjs/redux-devtools/blob/main/extension/docs/API/Arguments.md) for
a list of the specific options that are available.

Defaults to `true`.

### `duplicateMiddlewareCheck`

If enabled, the store will check the final middleware array to see if there are any duplicate middleware references. This will catch issues like accidentally adding the same RTK Query API middleware twice (such as adding both the base API middleware and an injected API middleware, which are actually the exact same function reference).

Defaults to `true`.

#### `trace`

The Redux DevTools Extension recently added [support for showing action stack traces](https://github.com/reduxjs/redux-devtools/blob/main/extension/docs/Features/Trace.md) that show exactly where each action was dispatched.
Capturing the traces can add a bit of overhead, so the DevTools Extension allows users to configure whether action stack traces are captured by [setting the 'trace' argument](https://github.com/reduxjs/redux-devtools/blob/main/extension/docs/API/Arguments.md#trace).
If the DevTools are enabled by passing `true` or an object, then `configureStore` will default to enabling capturing action stack traces in development mode only.

### `preloadedState`

An optional initial state value to be passed to the Redux `createStore` function.

### `enhancers`

A callback function to customize the array of enhancers.

Enhancers returned by this callback will be passed to [the Redux `compose` function](/api/compose), and the combined enhancer will be passed to `createStore`.

:::tip Dev Tools
This should _not_ include the Redux DevTools Extension `composeWithDevTools`, as this is already handled by `configureStore`.

Example: `enhancers: () => new Tuple(offline)` will result in a final setup of `[offline, devToolsExtension]`.
:::

If not provided, `configureStore` will call `getDefaultEnhancers` and use the array of enhancers it returns (including `applyMiddleware` with specified middleware).

Where you wish to add onto or customize the default enhancers, you may pass a callback function that will receive `getDefaultEnhancers` as its argument, and should return an enhancer array.

Example: `enhancers: (defaultEnhancers) => defaultEnhancers.prepend(offline)` will result in a final setup
of `[offline, applyMiddleware, devToolsExtension]`.

For more details on how the `enhancers` parameter works and the list of enhancers that are added by default, see the [`getDefaultEnhancers` docs page](./getDefaultEnhancers).

:::caution Middleware

If you don't use `getDefaultEnhancers` and instead return an array, the `applyMiddleware` enhancer will _not_ be used.

`configureStore` will warn in console if any middleware are provided (or left as default) but not included in the final list of enhancers.

```ts no-transpile
// warns - middleware customised but not included in final enhancers
configureStore({
  reducer,
  middleware: (getDefaultMiddleware) => getDefaultMiddleware().concat(logger),
  enhancers: [offline(offlineConfig)],
})

// fine - default enhancers included
configureStore({
  reducer,
  enhancers: (getDefaultEnhancers) =>
    getDefaultEnhancers().concat(offline(offlineConfig)),
})

// also allowed
configureStore({
  reducer,
  middleware: () => [],
  enhancers: () => [offline(offlineConfig)],
})
```

Note that if using TypeScript, the `middleware` option is required to be provided _before_ the enhancer option, as the type of `getDefaultEnhancers` depends on its result.

:::

:::note Tuple
TypeScript users are required to use a `Tuple` instance (if not using a `getDefaultEnhancers` result, which is already a `Tuple`), for better inference.

```ts no-transpile
import { configureStore, Tuple } from '@reduxjs/toolkit'

configureStore({
  reducer: rootReducer,
  enhancers: () => new Tuple(offline),
})
```

Javascript-only users are free to use a plain array if preferred.

:::

## Usage

### Basic Example

```ts
// file: reducers.ts noEmit
import type { Reducer } from '@reduxjs/toolkit'
declare const rootReducer: Reducer<{}>
export default rootReducer

// file: store.ts
import { configureStore } from '@reduxjs/toolkit'

import rootReducer from './reducers'

const store = configureStore({ reducer: rootReducer })
// The store now has redux-thunk added and the Redux DevTools Extension is turned on
```

### Full Example

```
/* Detailed source-code truncated for AI context efficiency. */
```

---

### Api CreateAction (`docs/api/createAction.mdx`)

---
id: createAction
title: createAction
sidebar_label: createAction
hide_title: true
---

&nbsp;

# `createAction`

A helper function for defining a Redux [action](/tutorials/fundamentals/part-2-concepts-data-flow) type and creator.

```js
function createAction(type, prepareAction?)
```

The usual way to define an action in Redux is to separately declare an _action type_ constant and an _action creator_ function for constructing actions of that type.

```ts
const INCREMENT = 'counter/increment'

function increment(amount: number) {
  return {
    type: INCREMENT,
    payload: amount,
  }
}

const action = increment(3)
// { type: 'counter/increment', payload: 3 }
```

The `createAction` helper combines these two declarations into one. It takes an action type and returns an action creator for that type. The action creator can be called either without arguments or with a `payload` to be attached to the action.

```ts
import { createAction } from '@reduxjs/toolkit'

const increment = createAction<number | undefined>('counter/increment')

let action = increment()
// { type: 'counter/increment' }

action = increment(3)
// returns { type: 'counter/increment', payload: 3 }

console.log(`The action type is: ${increment.type}`)
// 'The action type is: counter/increment'
```

## Using Prepare Callbacks to Customize Action Contents

By default, the generated action creators accept a single argument, which becomes `action.payload`. This requires the caller to construct the entire payload correctly and pass it in.

In many cases, you may want to write additional logic to customize the creation of the `payload` value, such as accepting multiple parameters for the action creator, generating a random ID, or getting the current timestamp. To do this, `createAction` accepts an optional second argument: a "prepare callback" that will be used to construct the payload value.

```ts
import { createAction, nanoid } from '@reduxjs/toolkit'

const addTodo = createAction('todos/add', function prepare(text: string) {
  return {
    payload: {
      text,
      id: nanoid(),
      createdAt: new Date().toISOString(),
    },
  }
})

console.log(addTodo('Write more docs'))
/**
 * {
 *   type: 'todos/add',
 *   payload: {
 *     text: 'Write more docs',
 *     id: '4AJvwMSWEHCchcWYga3dj',
 *     createdAt: '2019-10-03T07:53:36.581Z'
 *   }
 * }
 **/
```

If provided, all arguments from the action creator will be passed to the prepare callback, and it should return an object with the `payload` field (otherwise the payload of created actions will be `undefined`). Additionally, the object can have a `meta` and/or an `error` field that will also be added to created actions. `meta` may contain extra information about the action, `error` may contain details about the action failure. These three fields (`payload`, `meta` and `error`) adhere to the specification of [Flux Standard Actions](https://github.com/redux-utilities/flux-standard-action#actions).

**Note:** The type field will be added automatically.

## Usage with createReducer()

Action creators can be passed directly to `addCase` in a [createReducer()](createReducer.mdx) build callback.

```ts
import { createAction, createReducer } from '@reduxjs/toolkit'

const increment = createAction<number>('counter/increment')
const decrement = createAction<number>('counter/decrement')

const counterReducer = createReducer(0, (builder) => {
  builder.addCase(increment, (state, action) => state + action.payload)
  builder.addCase(decrement, (state, action) => state - action.payload)
})
```

:::warning Non-String Action Types
As of Redux 5.0, action types are _required_ to be strings. An error will be thrown by the store if a non-string action type reaches the original store dispatch.
:::

## actionCreator.match

Every generated actionCreator has a `.match(action)` method that can be used to determine if the passed action is of the same type as an action that would be created by the action creator.

This has different uses:

### As a TypeScript Type Guard

This `match` method is a [TypeScript type guard](https://www.typescriptlang.org/docs/handbook/2/narrowing.html#using-type-predicates) and can be used to discriminate the `payload` type of an action.

This behavior can be particularly useful when used in custom middlewares, where manual casts might be necessary otherwise.

```ts
import { createAction } from '@reduxjs/toolkit'
import type { Action } from '@reduxjs/toolkit'

const increment = createAction<number>('INCREMENT')

function someFunction(action: Action) {
  // accessing action.payload would result in an error here
  if (increment.match(action)) {
    // action.payload can be used as `number` here
  }
}
```

### With redux-observable

The `match` method can also be used as a filter method, which makes it powerful when used with redux-observable:

```ts
import { createAction } from '@reduxjs/toolkit'
import type { Action } from '@reduxjs/toolkit'
import type { Observable } from 'rxjs'
import { map, filter } from 'rxjs/operators'

const increment = createAction<number>('INCREMENT')

export const epic = (actions$: Observable<Action>) =>
  actions$.pipe(
    filter(increment.match),
    map((action) => {
      // action.payload can be safely used as number here (and will also be correctly inferred by TypeScript)
      // ...
    }),
  )
```

---

### Api CreateAsyncThunk (`docs/api/createAsyncThunk.mdx`)

---
id: createAsyncThunk
title: createAsyncThunk
sidebar_label: createAsyncThunk
hide_title: true
---

&nbsp;

# `createAsyncThunk`

## Overview

A function that accepts a Redux action type string and a callback function that should return a promise. It generates promise lifecycle action types based on the action type prefix that you pass in, and returns a thunk action creator that will run the promise callback and dispatch the lifecycle actions based on the returned promise.

This abstracts the standard recommended approach for handling async request lifecycles.

It does not generate any reducer functions, since it does not know what data you're fetching, how you want to track loading state, or how the data you return needs to be processed. You should write your own reducer logic that handles these actions, with whatever loading state and processing logic is appropriate for your own app.

:::tip

Redux Toolkit's [**RTK Query data fetching API**](../rtk-query/overview.md) is a purpose built data fetching and caching solution for Redux apps, and can **eliminate the need to write _any_ thunks or reducers to manage data fetching**. We encourage you to try it out and see if it can help simplify the data fetching code in your own apps!

:::

Sample usage:

```ts no-transpile {5-11,22-25,30}
import { createAsyncThunk, createSlice } from '@reduxjs/toolkit'
import { userAPI } from './userAPI'

// First, create the thunk
const fetchUserById = createAsyncThunk(
  'users/fetchByIdStatus',
  async (userId: number, thunkAPI) => {
    const response = await userAPI.fetchById(userId)
    return response.data
  },
)

interface UsersState {
  entities: User[]
  loading: 'idle' | 'pending' | 'succeeded' | 'failed'
}

const initialState = {
  entities: [],
  loading: 'idle',
} satisfies UserState as UsersState

// Then, handle actions in your reducers:
const usersSlice = createSlice({
  name: 'users',
  initialState,
  reducers: {
    // standard reducer logic, with auto-generated action types per reducer
  },
  extraReducers: (builder) => {
    // Add reducers for additional action types here, and handle loading state as needed
    builder.addCase(fetchUserById.fulfilled, (state, action) => {
      // Add user to the state array
      state.entities.push(action.payload)
    })
  },
})

// Later, dispatch the thunk as needed in the app
dispatch(fetchUserById(123))
```

## Parameters

`createAsyncThunk` accepts three parameters: a string action `type` value, a `payloadCreator` callback, and an `options` object.

### `type`

A string that will be used to generate additional Redux action type constants, representing the lifecycle of an async request:

For example, a `type` argument of `'users/requestStatus'` will generate these action types:

- `pending`: `'users/requestStatus/pending'`
- `fulfilled`: `'users/requestStatus/fulfilled'`
- `rejected`: `'users/requestStatus/rejected'`

### `payloadCreator`

A callback function that should return a promise containing the result of some asynchronous logic. It may also return a value synchronously. If there is an error, it should either return a rejected promise containing an `Error` instance or a plain value such as a descriptive error message or otherwise a resolved promise with a `RejectWithValue` argument as returned by the `thunkAPI.rejectWithValue` function.

The `payloadCreator` function can contain whatever logic you need to calculate an appropriate result. This could include a standard AJAX data fetch request, multiple AJAX calls with the results combined into a final value, interactions with React Native `AsyncStorage`, and so on.

The `payloadCreator` function will be called with two arguments:

- `arg`: a single value, containing the first parameter that was passed to the thunk action creator when it was dispatched. This is useful for passing in values like item IDs that may be needed as part of the request. If you need to pass in multiple values, pass them together in an object when you dispatch the thunk, like `dispatch(fetchUsers({status: 'active', sortBy: 'name'}))`.
- `thunkAPI`: an object containing all of the parameters that are normally passed to a Redux thunk function, as well as additional options:
  - `dispatch`: the Redux store `dispatch` method
  - `getState`: the Redux store `getState` method
  - `extra`: the "extra argument" given to the thunk middleware on setup, if available
  - `requestId`: a unique string ID value that was automatically generated to identify this request sequence
  - `signal`: an [`AbortController.signal` object](https://developer.mozilla.org/en-US/docs/Web/API/AbortController/signal) that may be used to see if another part of the app logic has marked this request as needing cancellation.
  - `rejectWithValue(value, [meta])`: rejectWithValue is a utility function that you can `return` (or `throw`) in your action creator to return a rejected response with a defined payload and meta. It will pass whatever value you give it and return it in the payload of the rejected action. If you also pass in a `meta`, it will be merged with the existing `rejectedAction.meta`.
  - `fulfillWithValue(value, meta)`: fulfillWithValue is a utility function that you can `return` in your action creator to `fulfill` with a value while having the ability of adding to `fulfilledAction.meta`.

The logic in the `payloadCreator` function may use any of these values as needed to calculate the result.

### Options

An object with the following optional fields:

- `condition(arg, { getState, extra } ): boolean | Promise<boolean>`: a callback that can be used to skip execution of the payload creator and all action dispatches, if desired. See [Canceling Before Execution](#canceling-before-execution) for a complete description.
- `dispatchConditionRejection`: if `condition()` returns `false`, the default behavior is that no actions will be dispatched at all. If you still want a "rejected" action to be dispatched when the thunk was canceled, set this flag to `true`.
- `idGenerator(arg): string`: a function to use when generating the `requestId` for the request sequence. Defaults to use [nanoid](./otherExports.mdx#nanoid), but you can implement your own ID generation logic.
- `serializeError(error: unknown) => any` to replace the internal `miniSerializeError` method with your own serialization logic.
- `getPendingMeta({ arg, requestId }, { getState, extra }): any`: a function to create an object that will be merged into the `pendingAction.meta` field.

## Return Value

`createAsyncThunk` returns a standard Redux thunk action creator. The thunk action creator function will have plain action creators for the `pending`, `fulfilled`, and `rejected` cases attached as nested fields.

Using the `fetchUserById` example above, `createAsyncThunk` will generate four functions:

- `fetchUserById`, the thunk action creator that kicks off the async payload callback you wrote
  - `fetchUserById.pending`, an action creator that dispatches an `'users/fetchByIdStatus/pending'` action
  - `fetchUserById.fulfilled`, an action creator that dispatches an `'users/fetchByIdStatus/fulfilled'` action
  - `fetchUserById.rejected`, an action creator that dispatches an `'users/fetchByIdStatus/rejected'` action

When dispatched, the thunk will:

- dispatch the `pending` action
- call the `payloadCreator` callback and wait for the returned promise to settle
- when the promise settles:
  - if the promise resolved successfully, dispatch the `fulfilled` action with the promise value as `action.payload`
  - if the promise resolved with a `rejectWithValue(value)` return value, dispatch the `rejected` action with the value passed into `action.payload` and 'Rejected' as `action.error.message`
  - if the promise failed and was not handled with `rejectWithValue`, dispatch the `rejected` action with a serialized version of the error value as `action.error`
- Return a fulfilled promise containing the final dispatched action (either the `fulfilled` or `rejected` action object)

## Thunk Dispatch Options

The returned thunk action creator accepts an optional second argument with the following options:

- `signal`: an optional [`AbortSignal`](https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal) that will be tracked by the internal abort signal (see [Canceling While Running](#canceling-while-running))

```ts no-transpile
const externalController = new AbortController()
dispatch(fetchUserById(123, { signal: externalController.signal }))
externalController.abort()
```

## Promise Lifecycle Actions

`createAsyncThunk` will generate three Redux action creators using [`createAction`](./createAction.mdx): `pending`, `fulfilled`, and `rejected`. Each lifecycle action creator will be attached to the returned thunk action creator so that your reducer logic can reference the action types and respond to the actions when dispatched. Each action object will contain the current unique `requestId` and `arg` values under `action.meta`.

The action creators will have these signatures:

```ts no-transpile
interface SerializedError {
  name?: string
  message?: string
  code?: string
  stack?: string
}

interface PendingAction<ThunkArg> {
  type: string
  payload: undefined
  meta: {
    requestId: string
    arg: ThunkArg
  }
}

interface FulfilledAction<ThunkArg, PromiseResult> {
  type: string
  payload: PromiseResult
  meta: {
    requestId: string
    arg: ThunkArg
  }
}

interface RejectedAction<ThunkArg> {
  type: string
  payload: undefined
  error: SerializedError | any
  meta: {
    requestId: string
    arg: ThunkArg
    aborted: boolean
    condition: boolean
  }
}

interface RejectedWithValueAction<ThunkArg, RejectedValue> {
  type: string
  payload: RejectedValue
  error: { message: 'Rejected' }
  meta: {
    requestId: string
    arg: ThunkArg
    aborted: boolean
  }
}

type Pending = <ThunkArg>(
  requestId: string,
  arg: ThunkArg,
) => PendingAction<ThunkArg>

type Fulfilled = <ThunkArg, PromiseResult>(
  payload: PromiseResult,
  requestId: string,
  arg: ThunkArg,
) => FulfilledAction<ThunkArg, PromiseResult>

type Rejected = <ThunkArg>(
  requestId: string,
  arg: ThunkArg,
) => RejectedAction<ThunkArg>

type RejectedWithValue = <ThunkArg, RejectedValue>(
  requestId: string,
  arg: ThunkArg,
) => RejectedWithValueAction<ThunkArg, RejectedValue>
```

To handle these actions in your reducers, reference the action creators in `createReducer` or `createSlice` using the "builder callback" notation.

```ts no-transpile {2,10}
const reducer1 = createReducer(initialState, (builder) => {
  builder.addCase(fetchUserById.fulfilled, (state, action) => {})
})

const reducer2 = createSlice({
  name: 'users',
  initialState,
  reducers: {},
  extraReducers: (builder) => {
    builder.addCase(fetchUserById.fulfilled, (state, action) => {})
  },
})
```

Additionally, a `settled` matcher is attached, for matching against both fulfilled and rejected actions. Conceptually this is similar to a `finally` block.

Make sure you use `addMatcher` instead of `addCase`, since `settled` is a matcher rather than an action creator.

```ts no-transpile {2,10}
const reducer1 = createReducer(initialState, (builder) => {
  builder.addMatcher(fetchUserById.settled, (state, action) => {})
})

const reducer2 = createSlice({
  name: 'users',
  initialState,
  reducers: {},
  extraReducers: (builder) => {
    builder.addMatcher(fetchUserById.settled, (state, action) => {})
  },
})
```

## Handling Thunk Results

### Unwrapping Result Actions

Thunks may return a value when dispatched. A common use case is to return a promise from the thunk, dispatch the thunk from a component, and then wait for the promise to resolve before doing additional work:

```ts no-transpile
const onClick = () => {
  dispatch(fetchUserById(userId)).then(() => {
    // do additional work
  })
}
```

The thunks generated by `createAsyncThunk` **will always return a resolved promise** with either the `fulfilled` action object or `rejected` action object inside, as appropriate.

The calling logic may wish to treat these actions as if they were the original promise contents. The promise returned by the dispatched thunk has an `unwrap` property which can be called to extract the `payload` of a `fulfilled` action or to throw either the `error` or, if available, `payload` created by `rejectWithValue` from a `rejected` action:

```ts no-transpile
// in the component

const onClick = () => {
  dispatch(fetchUserById(userId))
    .unwrap()
    .then((originalPromiseResult) => {
      // handle result here
    })
    .catch((rejectedValueOrSerializedError) => {
      // handle error here
    })
}
```

Or with async/await syntax:

```ts no-transpile
// in the component

const onClick = async () => {
  try {
    const originalPromiseResult = await dispatch(fetchUserById(userId)).unwrap()
    // handle result here
  } catch (rejectedValueOrSerializedError) {
    // handle error here
  }
}
```

Using the attached `.unwrap()` property is preferred in most cases, however Redux Toolkit also exports an `unwrapResult` function that can be used for a similar purpose:

```ts no-transpile
import { unwrapResult } from '@reduxjs/toolkit'

// in the component
const onClick = () => {
  dispatch(fetchUserById(userId))
    .then(unwrapResult)
    .then((originalPromiseResult) => {
      // handle result here
    })
    .catch((rejectedValueOrSerializedError) => {
      // handle result here
    })
}
```

Or with async/await syntax:

```ts no-transpile
import { unwrapResult } from '@reduxjs/toolkit'

// in the component
const onClick = async () => {
  try {
    const resultAction = await dispatch(fetchUserById(userId))
    const originalPromiseResult = unwrapResult(resultAction)
    // handle result here
  } catch (rejectedValueOrSerializedError) {
    // handle error here
  }
}
```

### Checking Errors After Dispatching

Note that this means **a failed request or error in a thunk will _never_ return a _rejected_ promise**. We assume that any failure is more of a handled error than an unhandled exception at this point. This is due to the fact that we want to prevent uncaught promise rejections for those who do not use the result of `dispatch`.

If your component needs to know if the request failed, use `.unwrap` or `unwrapResult` and handle the re-thrown error accordingly.

## Handling Thunk Errors

When your `payloadCreator` returns a rejected promise (such as a thrown error in an `async` function), the thunk will dispatch a `rejected` action containing an automatically-serialized version of the error as `action.error`. However, to ensure serializability, everything that does not match the `SerializedError` interface will have been removed from it:

```ts no-transpile
export interface SerializedError {
  name?: string
  message?: string
  stack?: string
  code?: string
}
```

If you need to customize the contents of the `rejected` action, you should catch any errors yourself, and then **return** a new value using the `thunkAPI.rejectWithValue` utility. Doing `return rejectWithValue(errorPayload)` will cause the `rejected` action to use that value as `action.payload`.

The `rejectWithValue` approach should also be used if your API response "succeeds", but contains some kind of additional error details that the reducer should know about. This is particularly common when expecting field-level validation errors from an API.

```ts no-transpile
const updateUser = createAsyncThunk(
  'users/update',
  async (userData, { rejectWithValue }) => {
    const { id, ...fields } = userData
    try {
      const response = await userAPI.updateById(id, fields)
      return response.data.user
    } catch (err) {
      // Use `err.response.data` as `action.payload` for a `rejected` action,
      // by explicitly returning it using the `rejectWithValue()` utility
      return rejectWithValue(err.response.data)
    }
  },
)
```

## Cancellation

### Canceling Before Execution

If you need to cancel a thunk before the payload creator is called, you may provide a `condition` callback as an option after the payload creator. The callback will receive the thunk argument and an object with `{getState, extra}` as parameters, and use those to decide whether to continue or not. If the execution should be canceled, the `condition` callback should return a literal `false` value or a promise that should resolve to `false`. If a promise is returned, the thunk waits for it to get fulfilled before dispatching the `pending` action, otherwise it proceeds with dispatching synchronously.

```ts no-transpile
const fetchUserById = createAsyncThunk(
  'users/fetchByIdStatus',
  async (userId: number, thunkAPI) => {
    const response = await userAPI.fetchById(userId)
    return response.data
  },
  {
    condition: (userId, { getState, extra }) => {
      const { users } = getState()
      const fetchStatus = users.requests[userId]
      if (fetchStatus === 'fulfilled' || fetchStatus === 'loading') {
        // Already fetched or in progress, don't need to re-fetch
        return false
      }
    },
  },
)
```

If `condition()` returns `false`, the default behavior is that no actions will be dispatched at all. If you still want a "rejected" action to be dispatched when the thunk was canceled, pass in `{condition, dispatchConditionRejection: true}`.

### Canceling While Running

If you want to cancel your running thunk before it has finished, you can use the `abort` method of the promise returned by `dispatch(fetchUserById(userId))`.

A real-life example of that would look like this:

```ts no-transpile
// file: store.ts noEmit
import { configureStore } from '@reduxjs/toolkit'
import type { Reducer } from '@reduxjs/toolkit'
import { useDispatch } from 'react-redux'

declare const reducer: Reducer<{}>
const store = configureStore({ reducer })
export const useAppDispatch = () => useDispatch<typeof store.dispatch>()

// file: slice.ts noEmit
import { createAsyncThunk } from '@reduxjs/toolkit'
export const fetchUserById = createAsyncThunk(
  'fetchUserById',
  (userId: string) => {
    /* ... */
  },
)

// file: MyComponent.ts
import { fetchUserById } from './slice'
import { useAppDispatch } from './store'
import React from 'react'

function MyComponent(props: { userId: string }) {
  const dispatch = useAppDispatch()
  React.useEffect(() => {
    // Dispatching the thunk returns a promise
    const promise = dispatch(fetchUserById(props.userId))
    return () => {
      // `createAsyncThunk` attaches an `abort()` method to the promise
      promise.abort()
    }
  }, [props.userId])
}
```

After a thunk has been cancelled this way, it will dispatch (and return) a `"thunkName/rejected"` action with an `AbortError` on the `error` property. The thunk will not dispatch any further actions.

Additionally, your `payloadCreator` can use the `AbortSignal` it is passed via `thunkAPI.signal` to actually cancel a costly asynchronous action.

The `fetch` api of modern browsers already comes with support for an `AbortSignal`:

```ts no-transpile
import { createAsyncThunk } from '@reduxjs/toolkit'

const fetchUserById = createAsyncThunk(
  'users/fetchById',
  async (userId: string, thunkAPI) => {
    const response = await fetch(`https://reqres.in/api/users/${userId}`, {
      signal: thunkAPI.signal,
    })
    return await response.json()
  },
)
```

### Checking Cancellation Status

### Reading the Signal Value

You can use the `signal.aborted` property to regularly check if the thunk has been aborted and in that case stop costly long-running work:

```ts no-transpile
import { createAsyncThunk } from '@reduxjs/toolkit'

const readStream = createAsyncThunk(
  'readStream',
  async (stream: ReadableStream, { signal }) => {
    const reader = stream.getReader()

    let done = false
    let result = ''

    while (!done) {
      if (signal.aborted) {
        throw new Error('stop the work, this has been aborted!')
      }
      const read = await reader.read()
      result += read.value
      done = read.done
    }
    return result
  },
)
```

#### Listening for Abort Events

You can also call `signal.addEventListener('abort', callback)` to have logic inside the thunk be notified when `promise.abort()` was called.
This can for example be used in conjunction with an axios `CancelToken`:

```ts no-transpile
import { createAsyncThunk } from '@reduxjs/toolkit'
import axios from 'axios'

const fetchUserById = createAsyncThunk(
  'users/fetchById',
  async (userId: string, { signal }) => {
    const source = axios.CancelToken.source()
    signal.addEventListener('abort', () => {
      source.cancel()
    })
    const response = await axios.get(`https://reqres.in/api/users/${userId}`, {
      cancelToken: source.token,
    })
    return response.data
  },
)
```

### Checking if a Promise Rejection was from an Error or Cancellation

To investigate behavior around thunk cancellation, you can inspect various properties on the `meta` object of the dispatched action.
If a thunk was cancelled, the result of the promise will be a `rejected` action (regardless of whether that action was actually dispatched to the store).

- If it was cancelled before execution, `meta.condition` will be true.
- If it was aborted while running, `meta.aborted` will be true.
- If neither of those is true, the thunk was not cancelled, it was simply rejected, either by a Promise rejection or `rejectWithValue`.
- If the thunk was not rejected, both `meta.aborted` and `meta.condition` will be `undefined`.

So if you wanted to test that a thunk was cancelled before executing, you can do the following:

```ts no-transpile
import { createAsyncThunk } from '@reduxjs/toolkit'

test('this thunk should always be skipped', async () => {
  const thunk = createAsyncThunk(
    'users/fetchById',
    async () => throw new Error('This promise should never be entered'),
    {
      condition: () => false,
    }
  )
  const result = await thunk()(dispatch, getState, null)

  expect(result.meta.condition).toBe(true)
  expect(result.meta.aborted).toBe(false)
})
```

## Examples

- Requesting a user by ID, with loading state, and only one request at a time:

```
/* Detailed source-code truncated for AI context efficiency. */
```

- Using rejectWithValue to access a custom rejected payload in a component

  _Note: this is a contrived example assuming our userAPI only ever throws validation-specific errors_

```
/* Detailed source-code truncated for AI context efficiency. */
```

---

### Api CreateDynamicMiddleware (`docs/api/createDynamicMiddleware.mdx`)

---
id: createDynamicMiddleware
title: createDynamicMiddleware
sidebar_label: createDynamicMiddleware
hide_title: true
---

&nbsp;

# `createDynamicMiddleware`

## Overview

A "meta-middleware" that allows adding middleware to the dispatch chain after store initialisation.

## Instance Creation

```ts no-transpile
import { createDynamicMiddleware, configureStore } from '@reduxjs/toolkit'

const dynamicMiddleware = createDynamicMiddleware()

const store = configureStore({
  reducer: {
    todos: todosReducer,
  },
  middleware: (getDefaultMiddleware) =>
    getDefaultMiddleware().prepend(dynamicMiddleware.middleware),
})
```

:::tip

It's possible to pass two type parameters to `createDynamicMiddleware`, `State` and `Dispatch`.

These are used by methods that receive middleware to ensure that the provided middleware are compatible with the types provided.

```ts no-transpile
const dynamicMiddleware = createDynamicMiddleware<State, Dispatch>()
```

However, if these values are derived from the store (as they should be), a circular type dependency is formed.

As a result, it's better to use the `withTypes` helper attached to `addMiddleware`, `withMiddleware` and `createDispatchWithMiddlewareHook`.

```ts no-transpile
import { createDynamicMiddleware } from '@reduxjs/toolkit/react'
import type { RootState, AppDispatch } from './store'

const dynamicMiddleware = createDynamicMiddleware()

const {
  middleware,
  addMiddleware,
  withMiddleware,
  createDispatchWithMiddlewareHook,
} = dynamicMiddleware

interface MiddlewareApiConfig {
  state: RootState
  dispatch: AppDispatch
}

export const addAppMiddleware = addMiddleware.withTypes<MiddlewareApiConfig>()

export const withAppMiddleware = withMiddleware.withTypes<MiddlewareApiConfig>()

export const createAppDispatchWithMiddlewareHook =
  createDispatchWithMiddlewareHook.withTypes<MiddlewareApiConfig>()

export default middleware
```

:::

## Dynamic Middleware Instance

The "dynamic middleware instance" returned from `createDynamicMiddleware` is an object similar to the object generated by `createListenerMiddleware`. The instance object is _not_ the actual Redux middleware itself. Rather, it contains the middleware and some instance methods used to add middleware to the chain.

```ts no-transpile
export type DynamicMiddlewareInstance<
  State = unknown,
  Dispatch extends ReduxDispatch<UnknownAction> = ReduxDispatch<UnknownAction>,
> = {
  middleware: DynamicMiddleware<State, Dispatch>
  addMiddleware: AddMiddleware<State, Dispatch>
  withMiddleware: WithMiddleware<State, Dispatch>
}
```

### `middleware`

The wrapper middleware instance, to add to the Redux store.

You can place this anywhere in the middleware chain, but note that all the middleware you inject into this instance will be contained within this position.

### `addMiddleware`

Injects a set of middleware into the instance.

```ts no-transpile
addMiddleware(logger, listenerMiddleware.instance)
```

:::note

- Middleware are compared by function reference, and each is only added to the chain once.

- Middleware are stored in an ES6 map, and are thus called in insertion order during dispatch.

:::

### `withMiddleware`

Accepts a set of middleware, and creates an action. When dispatched, it injects the middleware and returns a version of `dispatch` typed to be aware of any extensions added.

```ts no-transpile
const listenerDispatch = store.dispatch(
  withMiddleware(listenerMiddleware.middleware),
)

const unsubscribe = listenerDispatch(addListener({ type, effect }))
```

## React Integration

When imported from the React-specific entry point (`@reduxjs/toolkit/react`), the result of calling `createDynamicMiddleware` will have extra methods attached.

_These depend on having `react-redux` installed._

```ts no-transpile
interface ReactDynamicMiddlewareInstance<
  State = any,
  Dispatch extends ReduxDispatch<UnknownAction> = ReduxDispatch<UnknownAction>,
> extends DynamicMiddlewareInstance<State, Dispatch> {
  createDispatchWithMiddlewareHook: CreateDispatchWithMiddlewareHook<
    State,
    Dispatch
  >
  createDispatchWithMiddlewareHookFactory: (
    context?: Context<
      ReactReduxContextValue<State, ActionFromDispatch<Dispatch>>
    >,
  ) => CreateDispatchWithMiddlewareHook<State, Dispatch>
}
```

### `createDispatchWithMiddlewareHook`

Accepts a set of middleware, and returns a [`useDispatch`](https://react-redux.js.org/api/hooks#usedispatch) hook returning a `dispatch` typed to include extensions from provided middleware.

```ts no-transpile
const useListenerDispatch = createDispatchWithMiddlewareHook(
  listenerInstance.middleware,
)

const Component = () => {
  const listenerDispatch = useListenerDispatch()
  useEffect(() => {
    const unsubscribe = listenerDispatch(addListener({ type, effect }))
    return () => unsubscribe()
  }, [dispatch])
}
```

:::caution

Middleware is injected when `createDispatchWithMiddlewareHook` is called, not when the `useDispatch` hook is used.

:::

### `createDispatchWithMiddlewareHookFactory`

Accepts a React context instance, and returns a `createDispatchWithMiddlewareHook` built to use that context.

```ts no-transpile
const createDispatchWithMiddlewareHook =
  createDispatchWithMiddlewareHookFactory(context)
```

Useful if you're using a [custom context](https://react-redux.js.org/using-react-redux/accessing-store#providing-custom-context) for React Redux.

---

### Api CreateEntityAdapter (`docs/api/createEntityAdapter.mdx`)

---
id: createEntityAdapter
title: createEntityAdapter
sidebar_label: createEntityAdapter
hide_title: true
---

&nbsp;

# `createEntityAdapter`

## Overview

A function that generates a set of prebuilt reducers and selectors for performing CRUD operations on a [normalized state structure](/usage/structuring-reducers/normalizing-state-shape) containing instances of a particular type of data object. These reducer functions may be passed as case reducers to `createReducer` and `createSlice`. They may also be used as "mutating" helper functions inside of `createReducer` and `createSlice`.

This API was ported from [the `@ngrx/entity` library](https://ngrx.io/guide/entity) created by the NgRx maintainers, but has been significantly modified for use with Redux Toolkit. We'd like to thank the NgRx team for originally creating this API and allowing us to port and adapt it for our needs.

:::note
The term "Entity" is used to refer to a unique type of data object in an application. For example, in a blogging application, you might have `User`, `Post`, and `Comment` data objects, with many instances of each being stored in the client and persisted on the server. `User` is an "entity" - a unique type of data object that the application uses. Each unique instance of an entity is assumed to have a unique ID value in a specific field.

As with all Redux logic, [_only_ plain JS objects and arrays should be passed in to the store - **no class instances!**](/style-guide/#do-not-put-non-serializable-values-in-state-or-actions)

For purposes of this reference, we will use `Entity` to refer to the specific data type that is being managed by a copy of the reducer logic in a specific portion of the Redux state tree, and `entity` to refer to a single instance of that type. Example: in `state.users`, `Entity` would refer to the `User` type, and `state.users.entities[123]` would be a single `entity`.
:::

The methods generated by `createEntityAdapter` will all manipulate an "entity state" structure that looks like:

```js
{
  // The unique IDs of each item. Must be strings or numbers
  ids: []
  // A lookup table mapping entity IDs to the corresponding entity objects
  entities: {
  }
}
```

`createEntityAdapter` may be called multiple times in an application. If you are using it with plain JavaScript, you may be able to reuse a single adapter definition with multiple entity types if they're similar enough (such as all having an `entity.id` field). For [TypeScript usage](../usage/usage-with-typescript.md#createentityadapter), you will need to call `createEntityAdapter` a separate time for each distinct `Entity` type, so that the type definitions are inferred correctly.

Sample usage:

```ts
import {
  createEntityAdapter,
  createSlice,
  configureStore,
} from '@reduxjs/toolkit'

type Book = { bookId: string; title: string }

const booksAdapter = createEntityAdapter({
  // Assume IDs are stored in a field other than `book.id`
  selectId: (book: Book) => book.bookId,
  // Keep the "all IDs" array sorted based on book titles
  sortComparer: (a, b) => a.title.localeCompare(b.title),
})

const booksSlice = createSlice({
  name: 'books',
  initialState: booksAdapter.getInitialState(),
  reducers: {
    // Can pass adapter functions directly as case reducers.  Because we're passing this
    // as a value, `createSlice` will auto-generate the `bookAdded` action type / creator
    bookAdded: booksAdapter.addOne,
    booksReceived(state, action) {
      // Or, call them as "mutating" helpers in a case reducer
      booksAdapter.setAll(state, action.payload.books)
    },
  },
})

const store = configureStore({
  reducer: {
    books: booksSlice.reducer,
  },
})

type RootState = ReturnType<typeof store.getState>

console.log(store.getState().books)
// { ids: [], entities: {} }

// Can create a set of memoized selectors based on the location of this entity state
const booksSelectors = booksAdapter.getSelectors<RootState>(
  (state) => state.books,
)

// And then use the selectors to retrieve values
const allBooks = booksSelectors.selectAll(store.getState())
```

## Parameters

`createEntityAdapter` accepts a single options object parameter, with two optional fields inside.

### `selectId`

A function that accepts a single `Entity` instance, and returns the value of whatever unique ID field is inside. If not provided, the default implementation is `entity => entity.id`. If your `Entity` type keeps its unique ID values in a field other than `entity.id`, you **must** provide a `selectId` function.

### `sortComparer`

A callback function that accepts two `Entity` instances, and should return a standard `Array.sort()` numeric result (1, 0, -1) to indicate their relative order for sorting.

If provided, the `state.ids` array will be kept in sorted order based on comparisons of the entity objects, so that mapping over the IDs array to retrieve entities by ID should result in a sorted array of entities.

If not provided, the `state.ids` array will not be sorted, and no guarantees are made about the ordering. In other words, `state.ids` can be expected to behave like a standard Javascript array.

Note that sorting only kicks in when state is changed via one of the CRUD functions below (for example, `addOne()`, `updateMany()`).

## Return Value

A "entity adapter" instance. An entity adapter is a plain JS object (not a class) containing the generated reducer functions, the original provided `selectId` and `sortComparer` callbacks, a method to generate an initial "entity state" value, and functions to generate a set of globalized and non-globalized memoized selector functions for this entity type.

The adapter instance will include the following methods (additional referenced TypeScript types included):

```
/* Detailed source-code truncated for AI context efficiency. */
```

### CRUD Functions

The primary content of an entity adapter is a set of generated reducer functions for adding, updating, and removing entity instances from an entity state object:

- `addOne`: accepts a single entity, and adds it if it's not already present.
- `addMany`: accepts an array of entities or an object in the shape of `Record<EntityId, T>`, and adds them if not already present.
- `setOne`: accepts a single entity and adds or replaces it
- `setMany`: accepts an array of entities or an object in the shape of `Record<EntityId, T>`, and adds or replaces them.
- `setAll`: accepts an array of entities or an object in the shape of `Record<EntityId, T>`, and replaces all existing entities with the values in the array.
- `removeOne`: accepts a single entity ID value, and removes the entity with that ID if it exists.
- `removeMany`: accepts an array of entity ID values, and removes each entity with those IDs if they exist.
- `removeAll`: removes all entities from the entity state object.
- `updateOne`: accepts an "update object" containing an entity ID and an object containing one or more new field values to update inside a `changes` field, and performs a shallow update on the corresponding entity.
- `updateMany`: accepts an array of update objects, and performs shallow updates on all corresponding entities.
- `upsertOne`: accepts a single entity. If an entity with that ID exists, it will perform a shallow update and the specified fields will be merged into the existing entity, with any matching fields overwriting the existing values. If the entity does not exist, it will be added.
- `upsertMany`: accepts an array of entities or an object in the shape of `Record<EntityId, T>` that will be shallowly upserted.

:::info Should I add, set or upsert my entity?

All three options will insert _new_ entities into the list. However they differ in how they handle entities that already exist. If an entity **already exists**:

- `addOne` and `addMany` will do nothing with the new entity
- `setOne` and `setMany` will completely replace the old entity with the new one. This will also get rid of any properties on the entity that are not present in the new version of said entity.
- `upsertOne` and `upsertMany` will do a shallow copy to merge the old and new entities overwriting existing values, adding any that were not there and not touching properties not provided in the new entity.

:::

Each method has a signature that looks like:

```ts no-transpile
;(state: EntityState<T>, argument: TypeOrPayloadAction<Argument<T>>) =>
  EntityState<T>
```

In other words, they accept a state that looks like `{ids: [], entities: {}}`, and calculate and return a new state.

These CRUD methods may be used in multiple ways:

- They may be passed as case reducers directly to `createReducer` and `createSlice`.
- They may be used as "mutating" helper methods when called manually, such as a separate hand-written call to `addOne()` inside of an existing case reducer, if the `state` argument is actually an Immer `Draft` value.
- They may be used as immutable update methods when called manually, if the `state` argument is actually a plain JS object or array.

:::note
These methods do _not_ have corresponding Redux actions created - they are just standalone reducers / update logic. **It is entirely up to you to decide where and how to use these methods!** Most of the time, you will want to pass them to `createSlice` or use them inside another reducer.
:::

Each method will check to see if the `state` argument is an Immer `Draft` or not. If it is a draft, the method will assume that it's safe to continue mutating that draft further. If it is not a draft, the method will pass the plain JS value to Immer's `createNextState()`, and return the immutably updated result value.

The `argument` may be either a plain value (such as a single `Entity` object for `addOne()` or an `Entity[]` array for `addMany()`, or a `PayloadAction` action object with that same value as `action.payload`. This enables using them as both helper functions and reducers.

> **Note on shallow updates:** `updateOne`, `updateMany`, `upsertOne`, and `upsertMany` only perform shallow updates in a mutable manner. This means that if your update/upsert consists of an object that includes nested properties, the value of the incoming change will overwrite the **entire** existing nested object. This may be unintended behavior for your application. As a general rule, these methods are best used with [normalized data](../usage/usage-guide.md#managing-normalized-data) that _do not_ have nested properties.

### `getInitialState`

Returns a new entity state object like `{ids: [], entities: {}}`.

It accepts an optional object as an argument. The fields in that object will be merged into the returned initial state value. For example, perhaps you want your slice to also track some loading state:

```js
const booksSlice = createSlice({
  name: 'books',
  initialState: booksAdapter.getInitialState({
    loading: 'idle',
  }),
  reducers: {
    booksLoadingStarted(state, action) {
      // Can update the additional state field
      state.loading = 'pending'
    },
  },
})
```

You can also pass in an array of entities or a `Record<EntityId, T>` object to pre-populate the initial state with some entities:

```js
const booksSlice = createSlice({
  name: 'books',
  initialState: booksAdapter.getInitialState(
    {
      loading: 'idle',
    },
    [
      { id: 'a', title: 'First' },
      { id: 'b', title: 'Second' },
    ],
  ),
  reducers: {},
})
```

This is equivalent to calling:

```js
const initialState = booksAdapter.getInitialState({
  loading: 'idle',
})

const prePopulatedState = booksAdapter.setAll(initialState, [
  { id: 'a', title: 'First' },
  { id: 'b', title: 'Second' },
])
```

The first parameter can be `undefined` if no additional properties are needed.

### Selector Functions

The entity adapter will contain a `getSelectors()` function that returns a set of selectors that know how to read the contents of an entity state object:

- `selectIds`: returns the `state.ids` array.
- `selectEntities`: returns the `state.entities` lookup table.
- `selectAll`: maps over the `state.ids` array, and returns an array of entities in the same order.
- `selectTotal`: returns the total number of entities being stored in this state.
- `selectById`: given the state and an entity ID, returns the entity with that ID or `undefined`.

Each selector function will be created using the `createSelector` function from Reselect, to enable memoizing calculation of the results.

:::tip

The `createSelector` instance used can be replaced, by passing it as part of the options object (second parameter):

```js
import {
  createDraftSafeSelectorCreator,
  weakMapMemoize,
} from '@reduxjs/toolkit'

const createWeakMapDraftSafeSelector =
  createDraftSafeSelectorCreator(weakMapMemoize)

const simpleSelectors = booksAdapter.getSelectors(undefined, {
  createSelector: createWeakMapDraftSafeSelector,
})

const globalizedSelectors = booksAdapter.getSelectors((state) => state.books, {
  createSelector: createWeakMapDraftSafeSelector,
})
```

If no instance is passed, it will default to [`createDraftSafeSelector`](./createSelector.mdx#createdraftsafeselector).

:::

Because selector functions are dependent on knowing where in the state tree this specific entity state object is kept, `getSelectors()` can be called in two ways:

- If called without any arguments (or with undefined as the first parameter), it returns an "unglobalized" set of selector functions that assume their `state` argument is the actual entity state object to read from.
- It may also be called with a selector function that accepts the entire Redux state tree and returns the correct entity state object.

For example, the entity state for a `Book` type might be kept in the Redux state tree as `state.books`. You can use `getSelectors()` to read from that state in two ways:

```js
const store = configureStore({
  reducer: {
    books: booksReducer,
  },
})

const simpleSelectors = booksAdapter.getSelectors()
const globalizedSelectors = booksAdapter.getSelectors((state) => state.books)

// Need to manually pass the correct entity state object in to this selector
const bookIds = simpleSelectors.selectIds(store.getState().books)

// This selector already knows how to find the books entity state
const allBooks = globalizedSelectors.selectAll(store.getState())
```

## Notes

### Applying Multiple Updates

If `updateMany()` is called with multiple updates targeted to the same ID, they will be merged into a single update, with later updates overwriting the earlier ones.

For both `updateOne()` and `updateMany()`, changing the ID of one existing entity to match the ID of a second existing entity will cause the first to replace the second completely.

Additionally, if there is no item for that ID, the update will be silently ignored.

## Examples

Exercising several of the CRUD methods and selectors:

```
/* Detailed source-code truncated for AI context efficiency. */
```

---

### Api CreateListenerMiddleware (`docs/api/createListenerMiddleware.mdx`)

---
id: createListenerMiddleware
title: createListenerMiddleware
sidebar_label: createListenerMiddleware
hide_title: true
---

&nbsp;

# `createListenerMiddleware`

## Overview

A Redux middleware that lets you define "listener" entries that contain an "effect" callback with additional logic, and a way to specify when that callback should run based on dispatched actions or state changes.

It's intended to be a lightweight alternative to more widely used Redux async middleware like sagas and observables. While similar to thunks in level of complexity and concept, it can be used to replicate some common saga usage patterns.

Conceptually, you can think of this as being similar to React's `useEffect` hook, except that it runs logic in response to Redux store updates instead of component props/state updates.

Listener effect callbacks have access to `dispatch` and `getState`, similar to thunks. The listener also receives a set of async workflow functions like `take`, `condition`, `pause`, `fork`, and `unsubscribe`, which allow writing more complex async logic.

Listeners can be defined statically by calling `listenerMiddleware.startListening()` during setup, or added and removed dynamically at runtime with special `dispatch(addListener())` and `dispatch(removeListener())` actions.

### Basic Usage

```
/* Detailed source-code truncated for AI context efficiency. */
```

## `createListenerMiddleware`

Creates an instance of the middleware, which should then be added to the store via `configureStore`'s `middleware` parameter.

```ts no-transpile
const createListenerMiddleware = (options?: CreateMiddlewareOptions) =>
  ListenerMiddlewareInstance

interface CreateListenerMiddlewareOptions<ExtraArgument = unknown> {
  extra?: ExtraArgument
  onError?: ListenerErrorHandler
}

type ListenerErrorHandler = (
  error: unknown,
  errorInfo: ListenerErrorInfo,
) => void

interface ListenerErrorInfo {
  raisedBy: 'effect' | 'predicate'
}
```

### Middleware Options

- `extra`: an optional "extra argument" that will be injected into the `listenerApi` parameter of each listener. Equivalent to [the "extra argument" in the Redux Thunk middleware](/usage/writing-logic-thunks#injecting-config-values-into-thunks)
- `onError`: an optional error handler that gets called with synchronous and async errors raised by `listener` and synchronous errors thrown by `predicate`.

## Listener Middleware Instance

The "listener middleware instance" returned from `createListenerMiddleware` is an object similar to the "slice" objects generated by `createSlice`. The instance object is _not_ the actual Redux middleware itself. Rather, it contains the middleware and some instance methods used to add and remove listener entries within the middleware.

```ts no-transpile
interface ListenerMiddlewareInstance<
  State = unknown,
  Dispatch extends ThunkDispatch<State, unknown, UnknownAction> = ThunkDispatch<
    State,
    unknown,
    UnknownAction
  >,
  ExtraArgument = unknown,
> {
  middleware: ListenerMiddleware<State, Dispatch, ExtraArgument>
  startListening: (options: AddListenerOptions) => Unsubscribe
  stopListening: (
    options: AddListenerOptions & UnsubscribeListenerOptions,
  ) => boolean
  clearListeners: () => void
}
```

### `middleware`

The actual Redux middleware. Add this to the Redux store via [the `configureStore.middleware` option](./configureStore.mdx#middleware).

Since the listener middleware can receive "add" and "remove" actions containing functions, this should normally be added as the first middleware in the chain so that it is before the serializability check middleware.

```js
const store = configureStore({
  reducer: {
    todos: todosReducer,
  },
  // Add the listener middleware to the store.
  // NOTE: Since this can receive actions with functions inside,
  // it should go before the serializability check middleware
  middleware: (getDefaultMiddleware) =>
    getDefaultMiddleware().prepend(listenerMiddleware.middleware),
})
```

### `startListening`

Adds a new listener entry to the middleware. Typically used to "statically" add new listeners during application setup.

```ts no-transpile
const startListening = (options: AddListenerOptions) => UnsubscribeListener

interface AddListenerOptions {
  // Four options for deciding when the listener will run:

  // 1) Exact action type string match
  type?: string

  // 2) Exact action type match based on the RTK action creator
  actionCreator?: ActionCreator

  // 3) Match one of many actions using an RTK matcher
  matcher?: Matcher

  // 4) Return true based on a combination of action + state
  predicate?: ListenerPredicate

  // The actual callback to run when the action is matched
  effect: (action: Action, listenerApi: ListenerApi) => void | Promise<void>
}

type ListenerPredicate<Action extends ReduxAction, State> = (
  action: Action,
  currentState?: State,
  originalState?: State,
) => boolean

type UnsubscribeListener = (
  unsubscribeOptions?: UnsubscribeListenerOptions,
) => void

interface UnsubscribeListenerOptions {
  cancelActive?: true
}
```

**You must provide exactly _one_ of the four options for deciding when the listener will run: `type`, `actionCreator`, `matcher`, or `predicate`**. Every time an action is dispatched, each listener will be checked to see if it should run based on the current action vs the comparison option provided.

These are all acceptable:

```js
// 1) Action type string
listenerMiddleware.startListening({ type: 'todos/todoAdded', effect })
// 2) RTK action creator
listenerMiddleware.startListening({ actionCreator: todoAdded, effect })
// 3) RTK matcher function
listenerMiddleware.startListening({
  matcher: isAnyOf(todoAdded, todoToggled),
  effect,
})
// 4) Listener predicate
listenerMiddleware.startListening({
  predicate: (action, currentState, previousState) => {
    // return true when the listener should run
  },
  effect,
})
```

Note that the `predicate` option actually allows matching solely against state-related checks, such as "did `state.x` change" or "the current value of `state.x` matches some criteria", regardless of the actual action.

The ["matcher" utility functions included in RTK](./matching-utilities.mdx) are acceptable as either the `matcher` or `predicate` option.

The return value is an `unsubscribe()` callback that will remove this listener. By default, unsubscribing will _not_ cancel any active instances of the listener. However, you may also pass in `{cancelActive: true}` to cancel running instances.

If you try to add a listener entry but another entry with this exact function reference already exists, no new entry will be added, and the existing `unsubscribe` method will be returned.

The `effect` callback will receive the current action as its first argument, as well as a "listener API" object similar to the "thunk API" object in `createAsyncThunk`.

All listener predicates and callbacks are checked _after_ the root reducer has already processed the action and updated the state. The `listenerApi.getOriginalState()` method can be used to get the state value that existed before the action that triggered this listener was processed.

### `stopListening`

Removes a given listener entry.

It accepts the same arguments as `startListening()`. It checks for an existing listener entry by comparing the function references of `listener` and the provided `actionCreator/matcher/predicate` function or `type` string.

By default, this does _not_ cancel any active running instances. However, you may also pass in `{cancelActive: true}` to cancel running instances.

```ts no-transpile
const stopListening = (
  options: AddListenerOptions & UnsubscribeListenerOptions,
) => boolean

interface UnsubscribeListenerOptions {
  cancelActive?: true
}
```

Returns `true` if the listener entry has been removed, or `false` if no subscription matching the input provided has been found.

```js
// Examples:
// 1) Action type string
listenerMiddleware.stopListening({
  type: 'todos/todoAdded',
  listener,
  cancelActive: true,
})
// 2) RTK action creator
listenerMiddleware.stopListening({ actionCreator: todoAdded, effect })
// 3) RTK matcher function
listenerMiddleware.stopListening({ matcher, effect, cancelActive: true })
// 4) Listener predicate
listenerMiddleware.stopListening({ predicate, effect })
```

### `clearListeners`

Removes all current listener entries. It also cancels all active running instances of those listeners as well.

This is most likely useful for test scenarios where a single middleware or store instance might be used in multiple tests, as well as some app cleanup situations.

```ts no-transpile
const clearListeners = () => void;
```

## Action Creators

In addition to adding and removing listeners by directly calling methods on the listener instance, you can dynamically add and remove listeners at runtime by dispatching special "add" and "remove" actions. These are exported from the main RTK package as standard RTK-generated action creators.

### `addListener`

A standard RTK action creator, imported from the package. Dispatching this action tells the middleware to dynamically add a new listener at runtime. It accepts exactly the same options as `startListening()`

Dispatching this action returns an `unsubscribe()` callback from `dispatch`.

```js
// Per above, provide `predicate` or any of the other comparison options
const unsubscribe = store.dispatch(addListener({ predicate, effect }))
```

### `removeListener`

A standard RTK action creator, imported from the package. Dispatching this action tells the middleware to dynamically remove a listener at runtime. Accepts the same arguments as `stopListening()`.

By default, this does _not_ cancel any active running instances. However, you may also pass in `{cancelActive: true}` to cancel running instances.

Returns `true` if the listener entry has been removed, `false` if no subscription matching the input provided has been found.

```js
const wasRemoved = store.dispatch(
  removeListener({ predicate, effect, cancelActive: true }),
)
```

### `clearAllListeners`

A standard RTK action creator, imported from the package. Dispatching this action tells the middleware to remove all current listener entries. It also cancels all active running instances of those listeners as well.

```js
store.dispatch(clearAllListeners())
```

## Listener API

The `listenerApi` object is the second argument to each listener callback. It contains several utility functions that may be called anywhere inside the listener's logic.

```
/* Detailed source-code truncated for AI context efficiency. */
```

These can be divided into several categories.

### Store Interaction Methods

- `dispatch: Dispatch`: the standard `store.dispatch` method
- `getState: () => State`: the standard `store.getState` method
- `getOriginalState: () => State`: returns the store state as it existed when the action was originally dispatched, _before_ the reducers ran. (**Note**: this method can only be called synchronously, during the initial dispatch call stack, to avoid memory leaks. Calling it asynchronously will throw an error.)
- `extra: unknown`: the "extra argument" that was provided as part of the middleware setup, if any

`dispatch` and `getState` are exactly the same as in a thunk. `getOriginalState` can be used to compare the original state before the listener was started.

`extra` can be used to inject a value such as an API service layer into the middleware at creation time, and is accessible here.

### Listener Subscription Management

- `unsubscribe: () => void`: removes the listener entry from the middleware, and prevent future instances of the listener from running. (This does _not_ cancel any active instances.)
- `subscribe: () => void`: will re-subscribe the listener entry if it was previously removed, or no-op if currently subscribed
- `cancelActiveListeners: () => void`: cancels all other running instances of this same listener _except_ for the one that made this call. (The cancellation will only have a meaningful effect if the other instances are paused using one of the cancellation-aware APIs like `take/cancel/pause/delay` - see "Cancellation and Task Management" in the "Usage" section for more details)
- `cancel: () => void`: cancels the instance of this listener that made this call.
- `throwIfCancelled: () => void`: throws a `TaskAbortError` if the current listener instance was cancelled.
- `signal: AbortSignal`: An [`AbortSignal`](https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal) whose `aborted` property will be set to `true` if the listener execution is aborted or completed.

Dynamically unsubscribing and re-subscribing this listener allows for more complex async workflows, such as avoiding duplicate running instances by calling `listenerApi.unsubscribe()` at the start of a listener, or calling `listenerApi.cancelActiveListeners()` to ensure that only the most recent instance is allowed to complete.

### Conditional Workflow Execution

- `take: (predicate: ListenerPredicate, timeout?: number) => Promise<[Action, State, State] | null>`: returns a promise that will resolve when the `predicate` returns `true`. The return value is the `[action, currentState, previousState]` combination that the predicate saw as arguments. If a `timeout` is provided and expires first, the promise resolves to `null`.
- `condition: (predicate: ListenerPredicate, timeout?: number) => Promise<boolean>`: Similar to `take`, but resolves to `true` if the predicate succeeds, and `false` if a `timeout` is provided and expires first. This allows async logic to pause and wait for some condition to occur before continuing. See "Writing Async Workflows" below for details on usage.
- `delay: (timeoutMs: number) => Promise<void>`: returns a cancellation-aware promise that resolves after the timeout, or rejects if cancelled before the expiration
- `pause: (promise: Promise<T>) => Promise<T>`: accepts any promise, and returns a cancellation-aware promise that either resolves with the argument promise or rejects if cancelled before the resolution

These methods provide the ability to write conditional logic based on future dispatched actions and state changes. Both also accept an optional `timeout` in milliseconds.

`take` resolves to a `[action, currentState, previousState]` tuple or `null` if it timed out, whereas `condition` resolves to `true` if it succeeded or `false` if timed out.

`take` is meant for "wait for an action and get its contents", while `condition` is meant for checks like `if (await condition(predicate))`.

Both these methods are cancellation-aware, and will throw a `TaskAbortError` if the listener instance is cancelled while paused.

Note that both `take` and `condition` will only resolve **after the next action** has been dispatched. They do not resolve immediately even if their predicate would return true for the current state.

### Child Tasks

- `fork: (executor: (forkApi: ForkApi) => T | Promise<T>) => ForkedTask<T>`: Launches a "child task" that may be used to accomplish additional work. Accepts any sync or async function as its argument, and returns a `{result, cancel}` object that can be used to check the final status and return value of the child task, or cancel it while in-progress.

Child tasks can be launched, and waited on to collect their return values. The provided `executor` function will be called asynchronously with a `forkApi` object containing `{pause, delay, signal}`, allowing it to pause or check cancellation status. It can also make use of the `listenerApi` from the listener's scope.

An example of this might be a listener that forks a child task containing an infinite loop that listens for events from a server. The parent then uses `listenerApi.condition()` to wait for a "stop" action, and cancels the child task.

The task and result types are:

```ts no-transpile
interface ForkedTaskAPI {
  pause<W>(waitFor: Promise<W>): Promise<W>
  delay(timeoutMs: number): Promise<void>
  signal: AbortSignal
}

export type TaskResolved<T> = {
  readonly status: 'ok'
  readonly value: T
}

export type TaskRejected = {
  readonly status: 'rejected'
  readonly error: unknown
}

export type TaskCancelled = {
  readonly status: 'cancelled'
  readonly error: TaskAbortError
}

export type TaskResult<Value> =
  TaskResolved<Value> | TaskRejected | TaskCancelled

export interface ForkedTask<T> {
  result: Promise<TaskResult<T>>
  cancel(): void
}
```

## TypeScript Usage

The middleware code is fully TS-typed. However, the `startListening` and `addListener` functions do not know what the store's `RootState` type looks like by default, so `getState()` will return `unknown`.

To fix this, the middleware provides types for defining "pre-typed" versions of those methods, similar to the pattern used for defining pre-typed React-Redux hooks. We specifically recommend creating the middleware instance in a separate file from the actual `configureStore()` call:

```ts no-transpile
// listenerMiddleware.ts
import { createListenerMiddleware, addListener } from '@reduxjs/toolkit'
import type { RootState, AppDispatch } from './store'

declare type ExtraArgument = { foo: string }

export const listenerMiddleware = createListenerMiddleware()

export const startAppListening = listenerMiddleware.startListening.withTypes<
  RootState,
  AppDispatch,
  ExtraArgument
>()

export const addAppListener = addListener.withTypes<RootState, AppDispatch>()
```

Then import and use those pre-typed methods in your components.

## Usage Guide

### Overall Purpose

This middleware lets you run additional logic when some action is dispatched, as a lighter-weight alternative to middleware like sagas and observables that have both a heavy runtime bundle cost and a large conceptual overhead.

This middleware is not intended to handle all possible use cases. Like thunks, it provides you with a basic set of primitives (including access to `dispatch` and `getState`), and gives you freedom to write any sync or async logic you want. This is both a strength (you can do anything!) and a weakness (you can do anything, with no guard rails!).

The middleware includes several async workflow primitives that are sufficient to write equivalents to many Redux-Saga effects operators like `takeLatest`, `takeLeading`, and `debounce`, although none of those methods are directly included. (See [the listener middleware tests file for examples of how to write code equivalent to those effects](https://github.com/reduxjs/redux-toolkit/blob/03eafd5236f16574935cdf1c5958e32ee8cf3fbe/packages/toolkit/src/listenerMiddleware/tests/effectScenarios.test.ts#L74-L363).)

### Standard Usage Patterns

The most common expected usage is "run some logic after a given action was dispatched". For example, you could set up a simple analytics tracker by looking for certain actions and sending extracted data to the server, including pulling user details from the store:

```js
listenerMiddleware.startListening({
  matcher: isAnyOf(action1, action2, action3),
  effect: (action, listenerApi) => {
    const user = selectUserDetails(listenerApi.getState())

    const { specialData } = action.meta

    analyticsApi.trackUsage(action.type, user, specialData)
  },
})
```

However, the `predicate` option also allows triggering logic when some state value has changed, or when the state matches a particular condition:

```js
listenerMiddleware.startListening({
  predicate: (action, currentState, previousState) => {
    // Trigger logic whenever this field changes
    return currentState.counter.value !== previousState.counter.value
  },
  effect,
})

listenerMiddleware.startListening({
  predicate: (action, currentState, previousState) => {
    // Trigger logic after every action if this condition is true
    return currentState.counter.value > 3
  },
  effect,
})
```

You could also implement a generic API fetching capability, where the UI dispatches a plain action describing the type of resource to be requested, and the middleware automatically fetches it and dispatches a result action:

```js
listenerMiddleware.startListening({
  actionCreator: resourceRequested,
  effect: async (action, listenerApi) => {
    const { name, args } = action.payload
    listenerApi.dispatch(resourceLoading())

    const res = await serverApi.fetch(`/api/${name}`, ...args)
    listenerApi.dispatch(resourceLoaded(res.data))
  },
})
```

(That said, we would recommend use of RTK Query for any meaningful data fetching behavior - this is primarily an example of what you _could_ do in a listener.)

The `listenerApi.unsubscribe` method may be used at any time, and will remove the listener from handling any future actions. As an example, you could create a one-shot listener by unconditionally calling `unsubscribe()` in the body - the effect callback would run the first time the relevant action is seen, then immediately unsubscribe and never run again. (The middleware actually uses this technique internally for the `take/condition` methods)

### Writing Async Workflows with Conditions

One of the great strengths of both sagas and observables is their support for complex async workflows, including stopping and starting behavior based on specific dispatched actions. However, the weakness is that both require mastering a complex API with many unique operators (effects methods like `call()` and `fork()` for sagas, RxJS operators for observables), and both add a significant amount to application bundle size.

While the listener middleware is _not_ meant to fully replace sagas or observables, it does provide a carefully chosen set of APIs to implement long-running async workflows as well.

Listeners can use the `condition` and `take` methods in `listenerApi` to wait until some action is dispatched or state check is met. The `condition` method is directly inspired by [the `condition` function in Temporal.io's workflow API](https://docs.temporal.io/docs/typescript/workflows/#condition) (credit to [@swyx](https://twitter.com/swyx) for the suggestion!), and `take` is inspired by [the `take` effect from Redux-Saga](https://redux-saga.js.org/docs/api#takepattern).

The signatures are:

```ts no-transpile
type ConditionFunction<Action extends ReduxAction, State> = (
  predicate: ListenerPredicate<Action, State> | (() => boolean),
  timeout?: number,
) => Promise<boolean>

type TakeFunction<Action extends ReduxAction, State> = (
  predicate: ListenerPredicate<Action, State> | (() => boolean),
  timeout?: number,
) => Promise<[Action, State, State] | null>
```

You can use `await condition(somePredicate)` as a way to pause execution of your listener callback until some criteria is met.

The `predicate` will be called after every action is processed by the reducers, and should return `true` when the condition should resolve. (It is effectively a one-shot listener itself.) If a `timeout` number (in ms) is provided, the promise will resolve `true` if the `predicate` returns first, or `false` if the timeout expires. This allows you to write comparisons like `if (await condition(predicate, timeout))`.

This should enable writing longer-running workflows with more complex async logic, such as [the "cancellable counter" example from Redux-Saga](https://github.com/redux-saga/redux-saga/blob/1ecb1bed867eeafc69757df8acf1024b438a79e0/examples/cancellable-counter/src/sagas/index.js).

An example of `condition` usage, from the test suite:

```ts no-transpile
test('condition method resolves promise when there is a timeout', async () => {
  let finalCount = 0
  let listenerStarted = false

  listenerMiddleware.startListening({
    predicate: (action, currentState: CounterState) => {
      return increment.match(action) && currentState.value === 0
    },
    effect: async (action, listenerApi) => {
      listenerStarted = true
      // Wait for either the counter to hit 3, or 50ms to elapse
      const result = await listenerApi.condition(
        (action, currentState: CounterState) => {
          return currentState.value === 3
        },
        50,
      )

      // In this test, we expect the timeout to happen first
      expect(result).toBe(false)
      // Save the state for comparison outside the listener
      const latestState = listenerApi.getState()
      finalCount = latestState.value
    },
  })

  store.dispatch(increment())
  // The listener should have started right away
  expect(listenerStarted).toBe(true)

  store.dispatch(increment())

  // If we wait 150ms, the condition timeout will expire first
  await delay(150)
  // Update the state one more time to confirm the listener isn't checking it
  store.dispatch(increment())

  // Handled the state update before the delay, but not after
  expect(finalCount).toBe(2)
})
```

### Cancellation and Task Management

The listener middleware supports cancellation of running listener instances, `take/condition/pause/delay` functions, and "child tasks", with an implementation based on [`AbortController`](https://developer.mozilla.org/en-US/docs/Web/API/AbortController).

The `listenerApi.pause/delay()` functions provide a cancellation-aware way to have the current listener sleep. `pause()` accepts a promise, while `delay` accepts a timeout value. If the listener is cancelled while waiting, a `TaskAbortError` will be thrown. In addition, both `take` and `condition` support cancellation interruption as well.

`listenerApi.cancelActiveListeners()` will cancel _other_ existing instances that are running, while `listenerApi.cancel()` can be used to cancel the _current_ instance (which may be useful from a fork, which could be deeply nested and not able to directly throw a promise to break out of the effect execution). `listenerAPi.throwIfCancelled()` can also be useful to bail out of workflows in case cancellation happened while the effect was doing other work.

`listenerApi.fork()` can used to launch "child tasks" that can do additional work. These can be waited on to collect their results. An example of this might look like:

```ts no-transpile
listenerMiddleware.startListening({
  actionCreator: increment,
  effect: async (action, listenerApi) => {
    // Spawn a child task and start it immediately
    const task = listenerApi.fork(async (forkApi) => {
      // Artificially wait a bit inside the child
      await forkApi.delay(5)
      // Complete the child by returning a value
      return 42
    })

    const result = await task.result
    // Unwrap the child result in the listener
    if (result.status === 'ok') {
      // Logs the `42` result value that was returned
      console.log('Child succeeded: ', result.value)
    }
  },
})
```

### Complex Async Workflows

The provided async workflow primitives (`cancelActiveListeners`, `cancel`, `unsubscribe`, `subscribe`, `take`, `condition`, `pause`, `delay`) can be used to implement behavior that is equivalent to many of the more complex async workflow capabilities found in the Redux-Saga library. This includes effects such as `throttle`, `debounce`, `takeLatest`, `takeLeading`, and `fork/join`. Some examples from the test suite:

```
/* Detailed source-code truncated for AI context efficiency. */
```

As a more practical example: [this saga-based "long polling" loop](https://gist.github.com/markerikson/5203e71a69fa9dff203c9e27c3d84154) repeatedly asks the server for a message and then processes each response. The child loop is started on demand when a "start polling" action is dispatched, and the loop is cancelled when a "stop polling" action is dispatched.

That approach can be implemented via the listener middleware:

```ts no-transpile
// Track how many times each message was processed by the loop
const receivedMessages = {
  a: 0,
  b: 0,
  c: 0,
}

const eventPollingStarted = createAction('serverPolling/started')
const eventPollingStopped = createAction('serverPolling/stopped')

listenerMiddleware.startListening({
  actionCreator: eventPollingStarted,
  effect: async (action, listenerApi) => {
    // Only allow one instance of this listener to run at a time
    listenerApi.unsubscribe()

    // Start a child job that will infinitely loop receiving messages
    const pollingTask = listenerApi.fork(async (forkApi) => {
      try {
        while (true) {
          // Cancellation-aware pause for a new server message
          const serverEvent = await forkApi.pause(pollForEvent())
          // Process the message. In this case, just count the times we've seen this message.
          if (serverEvent.type in receivedMessages) {
            receivedMessages[
              serverEvent.type as keyof typeof receivedMessages
            ]++
          }
        }
      } catch (err) {
        if (err instanceof TaskAbortError) {
          // could do something here to track that the task was cancelled
        }
      }
    })

    // Wait for the "stop polling" action
    await listenerApi.condition(eventPollingStopped.match)
    pollingTask.cancel()
  },
})
```

### Adding Listeners Inside Components

Listeners can be added at runtime via `dispatch(addListener())`. This means that you can add listeners anywhere you have access to `dispatch`, and that includes React components.

Since dispatching `addListener` returns an `unsubscribe` callback, this naturally maps to the behavior of React `useEffect` hooks, which let you return a cleanup function. You can add a listener in an effect, and remove the listener when the hook is cleaned up.

The basic pattern might look like:

```js
useEffect(() => {
  // Could also just `return dispatch(addListener())` directly, but showing this
  // as a separate variable to be clear on what's happening
  const unsubscribe = dispatch(
    addListener({
      actionCreator: todoAdded,
      effect: (action, listenerApi) => {
        // do some useful logic here
      },
    }),
  )
  return unsubscribe
}, [])
```

While this pattern is _possible_, **we do not necessarily _recommend_ doing this!** The React and Redux communities have always tried to emphasize basing behavior on _state_ as much as possible. Having React components directly tie into the Redux action dispatch pipeline could potentially lead to codebases that are more difficult to maintain.

At the same time, this _is_ a valid technique, both in terms of API behavior and potential use cases. It's been common to lazy-load sagas as part of a code-split app, and that has often required some complex additional setup work to "inject" sagas. In contrast, `dispatch(addListener())` fits naturally into a React component's lifecycle.

So, while we're not specifically encouraging use of this pattern, it's worth documenting here so that users are aware of it as a possibility.

### Organizing Listeners in Files

As a starting point, **it's best to create the listener middleware in a separate file, such as `app/listenerMiddleware.ts`, rather than in the same file as the store**. This avoids any potential circular import problems from other files trying to import `middleware.addListener`.

From there, so far we've come up with three different ways to organize listener functions and setup.

First, you can import effect callbacks from slice files into the middleware file, and add the listeners:

```ts no-transpile title="app/listenerMiddleware.ts"
import { action1, listener1 } from '../features/feature1/feature1Slice'
import { action2, listener2 } from '../features/feature2/feature2Slice'

listenerMiddleware.startListening({ actionCreator: action1, effect: listener1 })
listenerMiddleware.startListening({ actionCreator: action2, effect: listener2 })
```

This is probably the simplest option, and mirrors how the store setup pulls together all the slice reducers to create the app.

The second option is the opposite: have the slice files import the middleware and directly add their listeners:

```ts no-transpile  title="features/feature1/feature1Slice.ts"
import { listenerMiddleware } from '../../app/listenerMiddleware'

const feature1Slice = createSlice(/* */)
const { action1 } = feature1Slice.actions

export default feature1Slice.reducer

listenerMiddleware.startListening({
  actionCreator: action1,
  effect: () => {},
})
```

This keeps all the logic in the slice, although it does lock the setup into a single middleware instance.

The third option is to create a setup function in the slice, but let the listener file call that on startup:

```ts no-transpile  title="features/feature1/feature1Slice.ts"
import type { AppStartListening } from '../../app/listenerMiddleware'

const feature1Slice = createSlice(/* */)
const { action1 } = feature1Slice.actions

export default feature1Slice.reducer

export const addFeature1Listeners = (startListening: AppStartListening) => {
  startListening({
    actionCreator: action1,
    effect: () => {},
  })
}
```

```ts no-transpile title="app/listenerMiddleware.ts"
import { addFeature1Listeners } from '../features/feature1/feature1Slice'

addFeature1Listeners(listenerMiddleware.startListening)
```

Feel free to use whichever of these approaches works best in your app.

---

### Api CreateReducer (`docs/api/createReducer.mdx`)

---
id: createReducer
title: createReducer
sidebar_label: createReducer
hide_title: true
---

&nbsp;

# `createReducer()`

## Overview

A utility that simplifies creating Redux reducer functions. It uses Immer internally to drastically simplify immutable update logic
by writing "mutative" code in your reducers, and supports directly mapping specific action types to case reducer functions
that will update the state when that action is dispatched.

Redux [reducers](/tutorials/fundamentals/part-3-state-actions-reducers) are often implemented using a `switch` statement, with one `case` for every handled action type.

```js
const initialState = { value: 0 }

function counterReducer(state = initialState, action) {
  switch (action.type) {
    case 'increment':
      return { ...state, value: state.value + 1 }
    case 'decrement':
      return { ...state, value: state.value - 1 }
    case 'incrementByAmount':
      return { ...state, value: state.value + action.payload }
    default:
      return state
  }
}
```

This approach works well, but is a bit boilerplate-y and error-prone. For instance, it is easy to forget the `default` case or
setting the initial state.

The `createReducer` helper streamlines the implementation of such reducers. It uses a "builder callback" notation to define handlers for specific action types, matching against a range of actions, or handling a default case. This is conceptually similar to a switch statement, but with better TS support.

With `createReducer`, your reducers instead look like:

```ts
import { createAction, createReducer } from '@reduxjs/toolkit'

interface CounterState {
  value: number
}

const increment = createAction('counter/increment')
const decrement = createAction('counter/decrement')
const incrementByAmount = createAction<number>('counter/incrementByAmount')

const initialState = { value: 0 } satisfies CounterState as CounterState

const counterReducer = createReducer(initialState, (builder) => {
  builder
    .addCase(increment, (state, action) => {
      state.value++
    })
    .addCase(decrement, (state, action) => {
      state.value--
    })
    .addCase(incrementByAmount, (state, action) => {
      state.value += action.payload
    })
})
```

## Usage with the "Builder Callback" Notation

[overloadSummary](docblock://createReducer.ts?token=createReducer)

### Parameters

[params](docblock://createReducer.ts?token=createReducer)

### Example Usage

[examples](docblock://createReducer.ts?token=createReducer)

### Builder Methods

### `builder.addCase`

[summary,remarks](docblock://mapBuilders.ts?token=ActionReducerMapBuilder.addCase)

#### Parameters

[params,examples](docblock://mapBuilders.ts?token=ActionReducerMapBuilder.addCase)

### `builder.addAsyncThunk`

[summary,remarks](docblock://mapBuilders.ts?token=ActionReducerMapBuilder.addAsyncThunk)

#### Parameters

[params,examples](docblock://mapBuilders.ts?token=ActionReducerMapBuilder.addAsyncThunk)

### `builder.addMatcher`

[summary,remarks](docblock://mapBuilders.ts?token=ActionReducerMapBuilder.addMatcher)

#### Parameters

[params,examples](docblock://mapBuilders.ts?token=ActionReducerMapBuilder.addMatcher)

### `builder.addDefaultCase`

[summary,remarks](docblock://mapBuilders.ts?token=ActionReducerMapBuilder.addDefaultCase)

#### Parameters

[params,examples](docblock://mapBuilders.ts?token=ActionReducerMapBuilder.addDefaultCase)

### Returns

The generated reducer function.

The reducer will have a `getInitialState` function attached that will return the initial state when called. This may be useful for tests or usage with React's `useReducer` hook:

```js
const counterReducer = createReducer(0, (builder) => {
  builder
    .addCase('increment', (state, action) => state + action.payload)
    .addCase('decrement', (state, action) => state - action.payload)
})

console.log(counterReducer.getInitialState()) // 0
```

### Example Usage

[examples](docblock://createReducer.ts?token=createReducer)

## Direct State Mutation

Redux requires reducer functions to be pure and treat state values as immutable. While this is essential for making state updates predictable and observable, it can sometimes make the implementation of such updates awkward. Consider the following example:

```ts
import { createAction, createReducer } from '@reduxjs/toolkit'

interface Todo {
  text: string
  completed: boolean
}

const addTodo = createAction<Todo>('todos/add')
const toggleTodo = createAction<number>('todos/toggle')

const todosReducer = createReducer([] as Todo[], (builder) => {
  builder
    .addCase(addTodo, (state, action) => {
      const todo = action.payload
      return [...state, todo]
    })
    .addCase(toggleTodo, (state, action) => {
      const index = action.payload
      const todo = state[index]
      return [
        ...state.slice(0, index),
        { ...todo, completed: !todo.completed },
        ...state.slice(index + 1),
      ]
    })
})
```

The `addTodo` reducer is straightforward if you know the [ES6 spread syntax](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax). However, the code for `toggleTodo` is much less straightforward, especially considering that it only sets a single flag.

To make things easier, `createReducer` uses [immer](https://github.com/mweststrate/immer) to let you write reducers as if they were mutating the state directly. In reality, the reducer receives a proxy state that translates all mutations into equivalent copy operations.

```ts
import { createAction, createReducer } from '@reduxjs/toolkit'

interface Todo {
  text: string
  completed: boolean
}

const addTodo = createAction<Todo>('todos/add')
const toggleTodo = createAction<number>('todos/toggle')

const todosReducer = createReducer([] as Todo[], (builder) => {
  builder
    .addCase(addTodo, (state, action) => {
      // This push() operation gets translated into the same
      // extended-array creation as in the previous example.
      const todo = action.payload
      state.push(todo)
    })
    .addCase(toggleTodo, (state, action) => {
      // The "mutating" version of this case reducer is much
      //  more direct than the explicitly pure one.
      const index = action.payload
      const todo = state[index]
      todo.completed = !todo.completed
    })
})
```

Writing "mutating" reducers simplifies the code. It's shorter, there's less indirection, and it eliminates common mistakes made while spreading nested state. However, the use of Immer does add some "magic", and Immer has its own nuances in behavior. You should read through [pitfalls mentioned in the immer docs](https://immerjs.github.io/immer/pitfalls) . Most importantly, **you need to ensure that you either mutate the `state` argument or return a new state, _but not both_**. For example, the following reducer would throw an exception if a `toggleTodo` action is passed:

```ts
import { createAction, createReducer } from '@reduxjs/toolkit'

interface Todo {
  text: string
  completed: boolean
}

const toggleTodo = createAction<number>('todos/toggle')

const todosReducer = createReducer([] as Todo[], (builder) => {
  builder.addCase(toggleTodo, (state, action) => {
    const index = action.payload
    const todo = state[index]

    // This case reducer both mutates the passed-in state...
    todo.completed = !todo.completed

    // ... and returns a new value. This will throw an
    // exception. In this example, the easiest fix is
    // to remove the `return` statement.
    return [...state.slice(0, index), todo, ...state.slice(index + 1)]
  })
})
```

## Multiple Case Reducer Execution

Originally, `createReducer` always matched a given action type to a single case reducer, and only that one case reducer would execute for a given action.

Using action matchers changes that behavior, as multiple matchers may handle a single action.

For any dispatched action, the behavior is:

- If there is an exact match for the action type, the corresponding case reducer will execute first
- Any matchers that return `true` will execute in the order they were defined
- If a default case reducer is provided, and _no_ case or matcher reducers ran, the default case reducer will execute
- If no case or matcher reducers ran, the original existing state value will be returned unchanged

The executing reducers form a pipeline, and each of them will receive the output of the previous reducer:

```ts
import { createReducer } from '@reduxjs/toolkit'

const reducer = createReducer(0, (builder) => {
  builder
    .addCase('increment', (state) => state + 1)
    .addMatcher(
      (action) => action.type.startsWith('i'),
      (state) => state * 5,
    )
    .addMatcher(
      (action) => action.type.endsWith('t'),
      (state) => state + 2,
    )
})

console.log(reducer(0, { type: 'increment' }))
// Returns 7, as the 'increment' case and both matchers all ran in sequence:
// - case 'increment": 0 => 1
// - matcher starts with 'i': 1 => 5
// - matcher ends with 't': 5 => 7
```

## Logging Draft State Values

It's very common for a developer to call `console.log(state)` during the development process. However, browsers display Proxies in a format that is hard to read, which can make console logging of Immer-based state difficult.

When using either `createSlice` or `createReducer`, you may use the [`current`](./otherExports.mdx#current) utility that we re-export from the [`immer` library](https://immerjs.github.io/immer/current). This utility creates a separate plain copy of the current Immer `Draft` state value, which can then be logged for viewing as normal.

```ts
import { createSlice, current } from '@reduxjs/toolkit'

const slice = createSlice({
  name: 'todos',
  initialState: [{ id: 1, title: 'Example todo' }],
  reducers: {
    addTodo: (state, action) => {
      console.log('before', current(state))
      state.push(action.payload)
      console.log('after', current(state))
    },
  },
})
```

---

### Api CreateSelector (`docs/api/createSelector.mdx`)

---
id: createSelector
title: createSelector
sidebar_label: createSelector
hide_title: true
---

&nbsp;

# `createSelector`

## Overview

The `createSelector` utility from the [Reselect library](https://github.com/reduxjs/reselect), re-exported for ease of use.

For more details on using `createSelector`, see:

- The [Reselect API documentation](https://github.com/reduxjs/reselect)
- [React-Redux docs: Hooks API - Using memoizing selectors](https://react-redux.js.org/next/api/hooks#using-memoizing-selectors)
- [Idiomatic Redux: Using Reselect Selectors for Encapsulation and Performance](https://blog.isquaredsoftware.com/2017/12/idiomatic-redux-using-reselect-selectors/)
- [React/Redux Links: Reducers and Selectors](https://github.com/markerikson/react-redux-links/blob/master/redux-reducers-selectors.md)

:::note
Prior to v0.7, RTK re-exported `createSelector` from [`selectorator`](https://github.com/planttheidea/selectorator), which
allowed using string keypaths as input selectors. This was removed, as it ultimately did not provide enough benefits, and
the string keypaths made static typing for selectors difficult.
:::

## `createDraftSafeSelector`

In general, we recommend against using selectors inside of reducers:

- Selectors typically expect the entire Redux state object as an argument, while slice reducers only have access to a specific subset of the entire Redux state
- Reselect's `createSelector` relies on reference comparisons to determine if inputs have changed, and if an Immer Proxy-wrapped draft value is passed in to a selector, the selector may see the same reference and think nothing has changed.

However, some users have requested the ability to create selectors that will work correctly inside of Immer-powered reducers. One use case for this might be collecting an ordered set of items when using `createEntityAdapter`, such as `const orderedTodos = todosSelectors.selectAll(todosState)`, and then using `orderedTodos` in the rest of the reducer logic.

Besides re-exporting `createSelector`, RTK also exports a wrapped version of `createSelector` named `createDraftSafeSelector` that allows you to create selectors that can safely be used inside of `createReducer` and `createSlice` reducers with Immer-powered mutable logic. When used with plain state values, the selector will still memoize normally based on the inputs. But, when used with Immer draft values, the selector will err on the side of recalculating the results, just to be safe.

All selectors created by `entityAdapter.getSelectors` are "draft safe" selectors by default.

Example:

```ts no-transpile
const selectSelf = (state: State) => state
const unsafeSelector = createSelector(selectSelf, (state) => state.value)
const draftSafeSelector = createDraftSafeSelector(
  selectSelf,
  (state) => state.value,
)

// in your reducer:

state.value = 1

const unsafe1 = unsafeSelector(state)
const safe1 = draftSafeSelector(state)

state.value = 2

const unsafe2 = unsafeSelector(state)
const safe2 = draftSafeSelector(state)
```

After executing that, `unsafe1` and `unsafe2` will be of the same value, because the memoized selector was
executed on the same object - but `safe2` will actually be different from `safe1` (with the updated value of `2`),
because the safe selector detected that it was executed on a Immer draft object and recalculated using the current
value instead of returning a cached value.

:::tip `createDraftSafeSelectorCreator`

RTK also exports a `createDraftSafeSelectorCreator` function, the "draft safe" equivalent of [`createSelectorCreator`](https://github.com/reduxjs/reselect#createselectorcreatormemoize-memoizeoptions).

```ts no-transpile
import {
  createDraftSafeSelectorCreator,
  weakMapMemoize,
} from '@reduxjs/toolkit'

const createWeakMapDraftSafeSelector =
  createDraftSafeSelectorCreator(weakMapMemoize)

const selectSelf = (state: State) => state
const draftSafeSelector = createWeakMapDraftSafeSelector(
  selectSelf,
  (state) => state.value,
)
```

:::

### Defining a Pre-Typed `createDraftSelector`

As of RTK 2.1, you can define a "pre-typed" version of `createDraftSafeSelector` that can have the type for `state` built in. This lets you set up those types once, so you don't have to repeat them each time you call `createDraftSafeSelector`.

```ts no-transpile
const createTypedDraftSafeSelector =
  createDraftSafeSelector.withTypes<RootState>()
```

Import and use the pre-typed `createTypedDraftSafeSelector` function, and it will automatically know that the `state` argument is of type `RootState`.

:::warning Known Limitations
Currently this approach only works if input selectors are provided as a single array.

If you pass the input selectors as separate inline arguments, the parameter types of the result function will not be inferred. As a workaround you can either

1. Wrap your input selectors in a single array
2. You can annotate the parameter types of the result function:

```ts no-transpile
import { createSelector } from 'reselect'

interface Todo {
  id: number
  completed: boolean
}

interface Alert {
  id: number
  read: boolean
}

export interface RootState {
  todos: Todo[]
  alerts: Alert[]
}

export const createTypedDraftSafeSelector =
  createDraftSafeSelector.withTypes<RootState>()

const selectTodoIds = createTypedDraftSafeSelector(
  // Type of `state` is set to `RootState`, no need to manually set the type
  (state) => state.todos,
  // ❌ Known limitation: Parameter types are not inferred in this scenario
  // so you will have to manually annotate them.
  (todos: Todo[]) => todos.map(({ id }) => id),
)
```

---

### Api CreateSlice (`docs/api/createSlice.mdx`)

---
id: createSlice
title: createSlice
sidebar_label: createSlice
hide_title: true
---

&nbsp;

# `createSlice`

A function that accepts an initial state, an object of reducer functions, and a "slice name",
and automatically generates action creators and action types that correspond to the reducers and state.

This API is the standard approach for writing Redux logic.

Internally, it uses [`createAction`](./createAction.mdx) and [`createReducer`](./createReducer.mdx), so
you may also use [Immer](../usage/immer-reducers.md) to write "mutating" immutable updates:

```ts
import { createSlice } from '@reduxjs/toolkit'
import type { PayloadAction } from '@reduxjs/toolkit'

interface CounterState {
  value: number
}

const initialState = { value: 0 } satisfies CounterState as CounterState

const counterSlice = createSlice({
  name: 'counter',
  initialState,
  reducers: {
    increment(state) {
      state.value++
    },
    decrement(state) {
      state.value--
    },
    incrementByAmount(state, action: PayloadAction<number>) {
      state.value += action.payload
    },
  },
})

export const { increment, decrement, incrementByAmount } = counterSlice.actions
export default counterSlice.reducer
```

## Parameters

`createSlice` accepts a single configuration object parameter, with the following options:

```ts no-transpile
function createSlice({
    // A name, used in action types
    name: string,
    // The initial state for the reducer
    initialState: State,
    // An object of "case reducers". Key names will be used to generate actions.
    reducers: Record<string, ReducerFunction | ReducerAndPrepareObject>,
    // A "builder callback" function used to add more reducers
    extraReducers?: (builder: ActionReducerMapBuilder<State>) => void,
    // A preference for the slice reducer's location, used by `combineSlices` and `slice.selectors`. Defaults to `name`.
    reducerPath?: string,
    // An object of selectors, which receive the slice's state as their first parameter.
    selectors?: Record<string, (sliceState: State, ...args: any[]) => any>,
})
```

### `initialState`

The initial state value for this slice of state.

This may also be a "lazy initializer" function, which should return an initial state value when called. This will be used whenever the reducer is called with `undefined` as its state value, and is primarily useful for cases like reading initial state from `localStorage`.

### `name`

A string name for this slice of state. Generated action type constants will use this as a prefix.

### `reducers`

An object containing Redux "case reducer" functions (functions intended to handle a specific action type, equivalent
to a single case statement in a switch).

The keys in the object will be used to generate string action type constants, and these will show up in the Redux
DevTools Extension when they are dispatched. Also, if any other part of the application happens to dispatch an action
with the exact same type string, the corresponding reducer will be run. Therefore, you should give the functions
descriptive names.

This object will be passed to [`createReducer`](./createReducer.mdx), so the reducers may safely "mutate" the
state they are given.

```ts
import { createSlice } from '@reduxjs/toolkit'

const counterSlice = createSlice({
  name: 'counter',
  initialState: 0,
  reducers: {
    increment: (state) => state + 1,
  },
})
// Will handle the action type `'counter/increment'`
```

#### Customizing Generated Action Creators

If you need to customize the creation of the payload value of an action creator by means of a [`prepare callback`](./createAction.mdx#using-prepare-callbacks-to-customize-action-contents), the value of the appropriate field of the `reducers` argument object should be an object instead of a function. This object must contain two properties: `reducer` and `prepare`. The value of the `reducer` field should be the case reducer function while the value of the `prepare` field should be the prepare callback function:

```ts
import { createSlice, nanoid } from '@reduxjs/toolkit'
import type { PayloadAction } from '@reduxjs/toolkit'

interface Item {
  id: string
  text: string
}

const todosSlice = createSlice({
  name: 'todos',
  initialState: [] as Item[],
  reducers: {
    addTodo: {
      reducer: (state, action: PayloadAction<Item>) => {
        state.push(action.payload)
      },
      prepare: (text: string) => {
        const id = nanoid()
        return { payload: { id, text } }
      },
    },
  },
})
```

### The `reducers` "creator callback" notation

Alternatively, the `reducers` field can be a callback which receives a "create" object.

The main benefit of this is that you can create [async thunks](./createAsyncThunk) as part of your slice (though for bundle size reasons, you [need a bit of setup for this](#createasyncthunk)). Types are also slightly simplified for prepared reducers.

```ts title="Creator callback for reducers"
import { createSlice, nanoid } from '@reduxjs/toolkit'

interface Item {
  id: string
  text: string
}

interface TodoState {
  loading: boolean
  todos: Item[]
}

const todosSlice = createSlice({
  name: 'todos',
  initialState: {
    loading: false,
    todos: [],
  } satisfies TodoState as TodoState,
  reducers: (create) => ({
    deleteTodo: create.reducer<number>((state, action) => {
      state.todos.splice(action.payload, 1)
    }),
    addTodo: create.preparedReducer(
      (text: string) => {
        const id = nanoid()
        return { payload: { id, text } }
      },
      // action type is inferred from prepare callback
      (state, action) => {
        state.todos.push(action.payload)
      },
    ),
    fetchTodo: create.asyncThunk(
      async (id: string, thunkApi) => {
        const res = await fetch(`myApi/todos?id=${id}`)
        return (await res.json()) as Item
      },
      {
        pending: (state) => {
          state.loading = true
        },
        rejected: (state, action) => {
          state.loading = false
        },
        fulfilled: (state, action) => {
          state.loading = false
          state.todos.push(action.payload)
        },
      },
    ),
  }),
})

export const { addTodo, deleteTodo, fetchTodo } = todosSlice.actions
```

#### Create Methods

#### `create.reducer`

A standard slice case reducer.

**Parameters**

- **reducer** The slice case reducer to use.

```ts no-transpile
create.reducer<Todo>((state, action) => {
  state.todos.push(action.payload)
})
```

#### `create.preparedReducer`

A [prepared](#customizing-generated-action-creators) reducer, to customize the action creator.

**Parameters**

- **prepareAction** The [`prepare callback`](./createAction#using-prepare-callbacks-to-customize-action-contents).
- **reducer** The slice case reducer to use.

The action passed to the case reducer will be inferred from the prepare callback's return.

```ts no-transpile
create.preparedReducer(
  (text: string) => {
    const id = nanoid()
    return { payload: { id, text } }
  },
  (state, action) => {
    state.todos.push(action.payload)
  },
)
```

#### `create.asyncThunk`

Creates an async thunk instead of an action creator.

:::caution Setup

To avoid pulling `createAsyncThunk` into the bundle size of `createSlice` by default, some extra setup is required to use `create.asyncThunk`.

The version of `createSlice` exported from RTK will throw an error if `create.asyncThunk` is called.

Instead, import `buildCreateSlice` and `asyncThunkCreator`, and create your own version of `createSlice`:

```ts
import { buildCreateSlice, asyncThunkCreator } from '@reduxjs/toolkit'

export const createAppSlice = buildCreateSlice({
  creators: { asyncThunk: asyncThunkCreator },
})
```

Then import this `createAppSlice` as needed instead of the exported version from RTK.

:::

**Parameters**

- **payloadCreator** The thunk [payload creator](./createAsyncThunk#payloadcreator).
- **config** The configuration object. (optional)

The configuration object can contain case reducers for each of the [lifecycle actions](./createAsyncThunk#promise-lifecycle-actions) (`pending`, `fulfilled`, and `rejected`), as well as a `settled` reducer that will run for both fulfilled and rejected actions (note that this will run _after_ any provided `fulfilled`/`rejected` reducers. Conceptually it can be thought of like a `finally` block.).

Each case reducer will be attached to the slice's `caseReducers` object, e.g. `slice.caseReducers.fetchTodo.fulfilled`.

The configuration object can also contain [`options`](./createAsyncThunk#options).

```ts no-transpile
create.asyncThunk(
  async (id: string, thunkApi) => {
    const res = await fetch(`myApi/todos?id=${id}`)
    return (await res.json()) as Item
  },
  {
    pending: (state) => {
      state.loading = true
    },
    rejected: (state, action) => {
      state.error = action.payload ?? action.error
    },
    fulfilled: (state, action) => {
      state.todos.push(action.payload)
    },
    settled: (state, action) => {
      state.loading = false
    }
    options: {
      idGenerator: uuid,
    },
  }
)
```

:::note

Typing for the `create.asyncThunk` works in the same way as [`createAsyncThunk`](../usage/usage-with-typescript#createasyncthunk), with one key difference.

A type for `state` and/or `dispatch` _cannot_ be provided as part of the `ThunkApiConfig`, as this would cause circular types.

Instead, it is necessary to assert the type when needed - `getState() as RootState`. You may also include an explicit return type for the payload function as well, in order to break the circular type inference cycle.

```ts no-transpile
create.asyncThunk<Todo, string, { rejectValue: { error: string } }>(
  // highlight-start
  // may need to include an explicit return type
  async (id: string, thunkApi): Promise<Todo> => {
    // Cast types for `getState` and `dispatch` manually
    const state = thunkApi.getState() as RootState
    const dispatch = thunkApi.dispatch as AppDispatch
    // highlight-end
    try {
      const todo = await fetchTodo()
      return todo
    } catch (e) {
      throw thunkApi.rejectWithValue({
        error: 'Oh no!',
      })
    }
  },
)
```

For common thunk API configuration options, a [`withTypes` helper](../usage/usage-with-typescript#defining-a-pre-typed-createasyncthunk) is provided:

```ts no-transpile
reducers: (create) => {
  const createAThunk = create.asyncThunk.withTypes<{
    rejectValue: { error: string }
  }>()

  return {
    fetchTodo: createAThunk<Todo, string>(async (id, thunkApi) => {
      throw thunkApi.rejectWithValue({
        error: 'Oh no!',
      })
    }),
    fetchTodos: createAThunk<Todo[], string>(async (id, thunkApi) => {
      throw thunkApi.rejectWithValue({
        error: 'Oh no, not again!',
      })
    }),
  }
}
```

:::

### `extraReducers`

Conceptually, each slice reducer "owns" its slice of state. There's also a natural correspondence between the update logic defined inside `reducers`, and the action types that are generated based on those.

However, there are many times that a Redux slice may also need to update its own state in response to action types that were defined elsewhere in the application (such as clearing many different kinds of data when a "user logged out" action is dispatched). This can include action types defined by another `createSlice` call, actions generated by a `createAsyncThunk`, RTK Query endpoint matchers, or any other action. In addition, one of the key concepts of Redux is that many slice reducers can independently respond to the same action type.

**`extraReducers` allows `createSlice` to respond and update its own state in response to other action types besides the types it has generated.**

As with the `reducers` field, each case reducer in `extraReducers` is [wrapped in Immer and may use "mutating" syntax to safely update the state inside](../usage/immer-reducers.md).

However, unlike the `reducers` field, each individual case reducer inside of `extraReducers` will _not_ generate a new action type or action creator.

If two fields from `reducers` and `extraReducers` happen to end up with the same action type string, the function from `reducers` will be used to handle that action type.

#### The `extraReducers` "builder callback" notation

Similar to `createReducer`, the `extraReducers` field uses a "builder callback" notation to define handlers for specific action types, matching against a range of actions, or handling a default case. This is conceptually similar to a switch statement, but with better TS support as it can infer the action type from the provided action creator. It's particularly useful for working with actions produced by `createAction` and `createAsyncThunk`.

[examples](docblock://createSlice.ts?token=CreateSliceOptions.extraReducers)

See [the "Builder Callback Notation" section of the `createReducer` reference](./createReducer.mdx#usage-with-the-builder-callback-notation) for details on how to use `builder.addCase`, `builder.addMatcher`, and `builder.addDefaultCase`

### `reducerPath`

Indicates a preference of where the slice should be located. Defaults to [`name`](#name).

This is used by `combineSlices` and the default generated `slice.selectors`.

### `selectors`

A set of selectors that receive the slice state as their first parameter, and any other parameters.

Each selector will have a corresponding key in the resulting [`selectors`](#selectors-1) object.

:::caution Circular types

It's fairly common to have selectors that use other selectors. This is still possible with slice selectors, but defining a selector without a return type can cause a circular type inference problem:

```ts no-transpile
const counterSlice = createSlice({
  name: 'counter',
  initialState: { value: 0 },
  reducers: {},
  selectors: {
    selectValue: (state) => state.value,
    // highlight-start
    // this creates a cycle, because it's inferring a type from the object we're creating here
    selectTimes: (state, times = 1) =>
      counterSlice.getSelectors().selectValue(state) * times,
    // highlight-end
  },
})
```

This cycle can be fixed by providing an explicit return type for the selector:

```ts no-transpile
const counterSlice = createSlice({
  name: 'counter',
  initialState: { value: 0 },
  reducers: {},
  selectors: {
    selectValue: (state) => state.value,
    // highlight-start
    // explicit return type means cycle is broken
    selectTimes: (state, times = 1): number =>
      counterSlice.getSelectors().selectValue(state) * times,
    // highlight-end
  },
})
```

This limitation may be also encountered when using a slice's `asyncThunk` creator.
In the same way, the issue is resolved by explicitly providing a type somewhere in the chain and breaking the cycle.

```ts no-transpile
const counterSlice = createSlice({
  name: 'counter',
  initialState: { value: 0 },
  reducers: (create) => ({
    getCountData: create.asyncThunk(async (_arg, { getState }) => {
      const currentCount = counterSlice.selectors.selectValue(
        getState() as RootState,
      )
      // highlight-start
      // this would cause a circular type, but the type annotation breaks the circle
      const result: Response = await fetch('api/' + currentCount)
      // highlight-end
      return result.json()
    }),
  }),
  selectors: {
    selectValue: (state) => state.value,
  },
})
```

:::

## Return Value

`createSlice` will return an object that looks like:

```ts no-transpile
{
    name: string,
    reducer: ReducerFunction,
    actions: Record<string, ActionCreator>,
    caseReducers: Record<string, CaseReducer>.
    getInitialState: () => State,
    reducerPath: string,
    selectSlice: Selector;
    selectors: Record<string, Selector>,
    getSelectors: (selectState: (rootState: RootState) => State) => Record<string, Selector>
    injectInto: (injectable: Injectable, config?: InjectConfig & { reducerPath?: string }) => InjectedSlice
}
```

Each function defined in the `reducers` argument will have a corresponding action creator generated using [`createAction`](./createAction.mdx)
and included in the result's `actions` field using the same function name.

The generated `reducer` function is suitable for passing to the Redux `combineReducers` function as a "slice reducer".

You may want to consider destructuring the action creators and exporting them individually, for ease of searching
for references in a larger codebase.

The functions passed to the `reducers` parameter can be accessed through the `caseReducers` return field. This can be particularly useful for testing or direct access to reducers created inline.

Result's function `getInitialState` provides access to the initial state value given to the slice. If a lazy state initializer was provided, it will be called and a fresh value returned.

`injectInto` creates an instance of the slice that is aware it's been injected - see [`combineSlices`](./combineSlices#slice-integration).

:::note
The result object is conceptually similar to a
["Redux duck" code structure](/faq/code-structure#what-should-my-file-structure-look-like-how-should-i-group-my-action-creators-and-reducers-in-my-project-where-should-my-selectors-go).
The actual code structure you use is up to you, but it's worth keeping in mind that actions are not exclusively limited to a single slice.
Any part of the reducer logic can (and should!) respond to any dispatched action.
:::

### Selectors

Slice selectors are written to expect the slice's state as their first parameter, but the slice may be located anywhere inside the store's root state.

As a result, there are two ways of getting final selectors:

#### `selectors`

Most commonly, the slice is reliably mounted under its [`reducerPath`](#reducerpath).

Following this, the slice has a `selectSlice` selector attached, which assumes that the slice is located under `rootState[slice.reducerPath]`.

`slice.selectors` then uses this selector to wrap each of the selectors provided.

```ts
import { createSlice } from '@reduxjs/toolkit'

interface CounterState {
  value: number
}

const counterSlice = createSlice({
  name: 'counter',
  initialState: { value: 0 } satisfies CounterState as CounterState,
  reducers: {
    // omitted
  },
  selectors: {
    selectValue: (sliceState) => sliceState.value,
  },
})

console.log(counterSlice.selectSlice({ counter: { value: 2 } })) // { value: 2 }

const { selectValue } = counterSlice.selectors

console.log(selectValue({ counter: { value: 2 } })) // 2
```

:::note

The original selector passed is attached to the wrapped selector as `.unwrapped`. For example:

```ts
import { createSlice, createSelector } from '@reduxjs/toolkit'

interface CounterState {
  value: number
}

const counterSlice = createSlice({
  name: 'counter',
  initialState: { value: 0 } satisfies CounterState as CounterState,
  reducers: {
    // omitted
  },
  selectors: {
    selectDouble: createSelector(
      (sliceState: CounterState) => sliceState.value,
      (value) => value * 2,
    ),
  },
})

const { selectDouble } = counterSlice.selectors

console.log(selectDouble({ counter: { value: 2 } })) // 4
console.log(selectDouble({ counter: { value: 3 } })) // 6
console.log(selectDouble.unwrapped.recomputations) // 2
```

:::

#### `getSelectors`

`slice.getSelectors` is called with a single parameter, a `selectState` callback. This function should receive the store root state (or whatever you expect to call the resulting selectors with) and return the slice state.

```ts no-transpile
const { selectValue } = counterSlice.getSelectors(
  (rootState: RootState) => rootState.aCounter,
)

console.log(selectValue({ aCounter: { value: 2 } })) // 2
```

If no `selectState` callback is passed, selectors will be returned as is - expecting the slice state as their first parameter (the same as calling `slice.getSelectors(state => state)`).

```ts no-transpile
const { selectValue } = counterSlice.getSelectors()

console.log(selectValue({ value: 2 })) // 2
```

:::note
The [`slice.selectors`](#selectors-2) object is the equivalent of calling

```ts no-transpile
const { selectValue } = counterSlice.getSelectors(counterSlice.selectSlice)
// or
const { selectValue } = counterSlice.getSelectors(
  (state: RootState) => state[counterSlice.reducerPath],
)
```

:::

## Examples

```
/* Detailed source-code truncated for AI context efficiency. */
```

---

### Api Errors (`docs/api/errors.mdx`)

---
id: errors
title: Production Error Codes
sidebar_label: Error Messages
slug: /errors
description: 'API > Production Error Codes: the full text of each Redux Toolkit production error code'
hide_table_of_contents: true
---

{/* The table component lives in the combined site (reduxjs/redux website/) */}
import ErrorCodesPage from '@site/src/components/ErrorCodesPage'
import errorCodes from '../../errors.json'

# Production Error Codes

When Redux Toolkit is built and running in production, error text is replaced by indexed error codes to save on bundle size. These errors will provide a link to this page with more information about the error below.

<ErrorCodesPage errorCodes={errorCodes} />

---

### Api GetDefaultEnhancers (`docs/api/getDefaultEnhancers.mdx`)

---
id: getDefaultEnhancers
title: getDefaultEnhancers
sidebar_label: getDefaultEnhancers
hide_title: true
---

&nbsp;

# `getDefaultEnhancers`

Returns an array containing the default list of enhancers.

## Intended Usage

By default, [`configureStore`](./configureStore.mdx) adds some enhancers to the Redux store setup automatically.

```js
const store = configureStore({
  reducer: rootReducer,
})

// Store has enhancers added, because the enhancer list was not customized
```

If you want to customise the list of enhancers, you can supply an array of enhancer functions to `configureStore`:

```js
const store = configureStore({
  reducer: rootReducer,
  enhancers: () => new Tuple(offline(offlineConfig)),
})

// store specifically has the offline enhancer applied
```

However, when you supply the `enhancer` option, you are responsible for defining _all_ the enhancers you want added
to the store (with the exception of the [devtools](./configureStore#devtools)). `configureStore` will not add any extra enhancers beyond what you listed, **including the middleware enhancer**.

`getDefaultEnhancers` is useful if you want to add some custom enhancers, but also still want to have the default
enhancers added as well:

```ts no-transpile
import { configureStore } from '@reduxjs/toolkit'
import { offline } from '@redux-offline/redux-offline'
import offlineConfig from '@redux-offline/redux-offline/lib/defaults'

import rootReducer from './reducer'

const store = configureStore({
  reducer: rootReducer,
  enhancers: (getDefaultEnhancers) =>
    getDefaultEnhancers().concat(offline(offlineConfig)),
})

// Store has all of the default middleware + enhancers added, _plus_ the offline enhancer
```

## Included Default Enhancers

The resulting array will always contain the `applyMiddleware` enhancer created based on the `configureStore`'s `middleware` field.

Additionally, the [`autoBatchEnhancer`](./autoBatchEnhancer.mdx) is included, to allow for "batching" of low priority action updates. This is used by [RTK Query](/rtk-query/overview.md) and should improve performance when using it.

Currently, the return value is

```js
const enhancers = [applyMiddleware, autoBatchEnhancer]
```

## Customising the Included Enhancers

`getDefaultEnhancers` accepts an options object that allows customizing each enhancer (excluding the middleware enhancer) in two ways:

- Each enhancer can be excluded from the result array by passing `false` for its corresponding field
- Each enhancer can have its options customized by passing the matching options object for its corresponding field

This example shows customising the autoBatch enhancer:

```ts
// file: reducer.ts noEmit

export default function rootReducer(state = {}, action: any) {
  return state
}

// file: store.ts
import rootReducer from './reducer'
import { configureStore } from '@reduxjs/toolkit'

const store = configureStore({
  reducer: rootReducer,
  enhancers: (getDefaultEnhancers) =>
    getDefaultEnhancers({
      autoBatch: { type: 'tick' },
    }),
})
```

## API Reference

```ts no-transpile
interface AutoBatchOptions {
  // see "autoBatchEnhancer" page for options
}

interface GetDefaultEnhancersOptions {
  autoBatch?: boolean | AutoBatchOptions
}

function getDefaultEnhancers<M extends Middlewares<any>>(
  options: GetDefaultEnhancersOptions = {},
): EnhancerArray<[StoreEnhancer<{ dispatch: ExtractDispatchExtensions<M> }>]>
```

---

### Api GetDefaultMiddleware (`docs/api/getDefaultMiddleware.mdx`)

---
id: getDefaultMiddleware
title: getDefaultMiddleware
sidebar_label: getDefaultMiddleware
hide_title: true
---

&nbsp;

# `getDefaultMiddleware`

Returns an array containing the default list of middleware.

## Intended Usage

By default, [`configureStore`](./configureStore.mdx) adds some middleware to the Redux store setup automatically.

```js
const store = configureStore({
  reducer: rootReducer,
})

// Store has middleware added, because the middleware list was not customized
```

If you want to customize the list of middleware, you can supply an array of middleware functions to `configureStore`:

```js
const store = configureStore({
  reducer: rootReducer,
  middleware: () => new Tuple(thunk, logger),
})

// Store specifically has the thunk and logger middleware applied
```

However, when you supply the `middleware` option, you are responsible for defining _all_ the middleware you want added
to the store. `configureStore` will not add any extra middleware beyond what you listed.

`getDefaultMiddleware` is useful if you want to add some custom middleware, but also still want to have the default
middleware added as well:

```ts no-transpile
import { configureStore } from '@reduxjs/toolkit'

import logger from 'redux-logger'

import rootReducer from './reducer'

const store = configureStore({
  reducer: rootReducer,
  middleware: (getDefaultMiddleware) => getDefaultMiddleware().concat(logger),
})

// Store has all of the default middleware added, _plus_ the logger middleware
```

It is preferable to use the chainable `.concat(...)` and `.prepend(...)` methods of the returned `Tuple` instead of the array spread operator, as the latter can lose valuable TS type information under some circumstances.

## Included Default Middleware

### Development

One of the goals of Redux Toolkit is to provide opinionated defaults and prevent common mistakes. As part of that,
`getDefaultMiddleware` includes some middleware that are added **in development builds of your app only** to
provide runtime checks for three common issues:

- [Immutability check middleware](./immutabilityMiddleware.mdx): deeply compares
  state values for mutations. It can detect mutations in reducers during a dispatch, and also mutations that occur between
  dispatches (such as in a component or a selector). When a mutation is detected, it will throw an error and indicate the key
  path for where the mutated value was detected in the state tree. (Forked from [`redux-immutable-state-invariant`](https://github.com/leoasis/redux-immutable-state-invariant).)

- [Serializability check middleware](./serializabilityMiddleware.mdx): a custom middleware created specifically for use in Redux Toolkit. Similar in
  concept to `immutable-state-invariant`, but deeply checks your state tree and your actions for non-serializable values
  such as functions, Promises, Symbols, and other non-plain-JS-data values. When a non-serializable value is detected, a
  console error will be printed with the key path for where the non-serializable value was detected.

- [Action creator check middleware](./actionCreatorMiddleware.mdx): another custom middleware created specifically for use in Redux Toolkit.
  Identifies when an action creator was mistakenly dispatched without being called, and warns to console with the action type.

In addition to these development tool middleware, it also adds [`redux-thunk`](https://github.com/reduxjs/redux-thunk)
by default, since thunks are the basic recommended side effects middleware for Redux.

Currently, the return value is:

```js
const middleware = [
  actionCreatorInvariant,
  immutableStateInvariant,
  thunk,
  serializableStateInvariant,
]
```

### Production

Currently, the return value is:

```js
const middleware = [thunk]
```

## Customizing the Included Middleware

`getDefaultMiddleware` accepts an options object that allows customizing each middleware in two ways:

- Each middleware can be excluded from the result array by passing `false` for its corresponding field
- Each middleware can have its options customized by passing the matching options object for its corresponding field

This example shows excluding the serializable state check middleware, and passing a specific value for the thunk
middleware's "extra argument":

```ts
// file: reducer.ts noEmit

export default function rootReducer(state = {}, action: any) {
  return state
}

// file: api.ts noEmit

export declare const myCustomApiService: any

// file: store.ts

import { configureStore } from '@reduxjs/toolkit'
import rootReducer from './reducer'
import { myCustomApiService } from './api'

const store = configureStore({
  reducer: rootReducer,
  middleware: (getDefaultMiddleware) =>
    getDefaultMiddleware({
      thunk: {
        extraArgument: myCustomApiService,
      },
      serializableCheck: false,
    }),
})
```

## API Reference

```ts no-transpile
interface ThunkOptions<E = any> {
  extraArgument: E
}

interface ImmutableStateInvariantMiddlewareOptions {
  // See "Immutability Middleware" page for definition
}

interface SerializableStateInvariantMiddlewareOptions {
  // See "Serializability Middleware" page for definition
}

interface ActionCreatorInvariantMiddlewareOptions {
  // See "Action Creator Middleware" page for definition
}

interface GetDefaultMiddlewareOptions {
  thunk?: boolean | ThunkOptions
  immutableCheck?: boolean | ImmutableStateInvariantMiddlewareOptions
  serializableCheck?: boolean | SerializableStateInvariantMiddlewareOptions
  actionCreatorCheck?: boolean | ActionCreatorInvariantMiddlewareOptions
}

function getDefaultMiddleware<S = any>(
  options: GetDefaultMiddlewareOptions = {},
): Middleware<{}, S>[]
```

---

### Api ImmutabilityMiddleware (`docs/api/immutabilityMiddleware.mdx`)

---
id: immutabilityMiddleware
title: Immutability Middleware
sidebar_label: Immutability Middleware
hide_title: true
---

&nbsp;

# Immutability Middleware

A port of the [`redux-immutable-state-invariant`](https://github.com/leoasis/redux-immutable-state-invariant) middleware, customized for use with Redux Toolkit. Any detected mutations will be thrown as errors.

This middleware is added to the store by default by [`configureStore`](./configureStore.mdx) and [`getDefaultMiddleware`](./getDefaultMiddleware.mdx).

You can customize the behavior of this middleware by passing any of the supported options as the `immutableCheck` value for `getDefaultMiddleware`.

## Options

```ts no-transpile
type IsImmutableFunc = (value: any) => boolean

interface ImmutableStateInvariantMiddlewareOptions {
  /**
    Callback function to check if a value is considered to be immutable.
    This function is applied recursively to every value contained in the state.
    The default implementation will return true for primitive types
    (like numbers, strings, booleans, null and undefined).
   */
  isImmutable?: IsImmutableFunc
  /**
    An array of dot-separated path strings or RegExps that match named nodes from
    the root state to ignore when checking for immutability.
    Defaults to undefined
   */
  ignoredPaths?: (string | RegExp)[]
  /** Print a warning if checks take longer than N ms. Default: 32ms */
  warnAfter?: number
}
```

## Exports

### `createImmutableStateInvariantMiddleware`

Creates an instance of the immutability check middleware, with the given options.

You will most likely not need to call this yourself, as `getDefaultMiddleware` already does so.

Example:

```ts
// file: exampleSlice.ts

import { createSlice } from '@reduxjs/toolkit'

export const exampleSlice = createSlice({
  name: 'example',
  initialState: {
    user: 'will track changes',
    ignoredPath: 'single level',
    ignoredNested: {
      one: 'one',
      two: 'two',
    },
  },
  reducers: {},
})

export default exampleSlice.reducer

// file: store.ts

import {
  configureStore,
  createImmutableStateInvariantMiddleware,
  Tuple,
} from '@reduxjs/toolkit'

import exampleSliceReducer from './exampleSlice'

const immutableInvariantMiddleware = createImmutableStateInvariantMiddleware({
  ignoredPaths: ['ignoredPath', 'ignoredNested.one', 'ignoredNested.two'],
})

const store = configureStore({
  reducer: exampleSliceReducer,
  // Note that this will replace all default middleware
  middleware: () => new Tuple(immutableInvariantMiddleware),
})
```

doing the same without removing all other middlewares, using [getDefaultMiddleware](./getDefaultMiddleware):

```ts
// file: exampleSlice.ts noEmit

import { createSlice } from '@reduxjs/toolkit'

export const exampleSlice = createSlice({
  name: 'example',
  initialState: {
    user: 'will track changes',
    ignoredPath: 'single level',
    ignoredNested: {
      one: 'one',
      two: 'two',
    },
  },
  reducers: {},
})

export default exampleSlice.reducer

// file: store.ts
import { configureStore } from '@reduxjs/toolkit'

import exampleSliceReducer from './exampleSlice'

const store = configureStore({
  reducer: exampleSliceReducer,
  // This replaces the original default middleware with the customized versions
  middleware: (getDefaultMiddleware) =>
    getDefaultMiddleware({
      immutableCheck: {
        ignoredPaths: ['ignoredPath', 'ignoredNested.one', 'ignoredNested.two'],
      },
    }),
})
```

### `isImmutableDefault`

Default implementation of the "is this value immutable?" check. Currently implemented as:

```js
return (
  typeof value !== 'object' || value === null || typeof value === 'undefined'
)
```

This will return true for primitive types (like numbers, strings, booleans, null and undefined)

---

### Api Matching Utilities (`docs/api/matching-utilities.mdx`)

---
id: matching-utilities
title: Matching Utilities
sidebar_label: Matching Utilities
hide_title: true
---

&nbsp;

# Matching Utilities

Redux Toolkit exports several type-safe action matching utilities that you can leverage when checking for specific kinds of actions. These are primarily useful for the `builder.addMatcher()` cases in `createSlice` and `createReducer`, as well as when writing custom middleware.

### General Purpose

- [`isAllOf`](#isallof) - returns true when **all** conditions are met
- [`isAnyOf`](#isanyof) - returns true when **at least one of** the conditions are met

### `createAsyncThunk`-specific matchers

All these matchers can either be called with one or more thunks as arguments, in which case they will return a matcher function for that condition and thunks, or with one actions, in which case they will match for any thunk action with said condition.

- [`isAsyncThunkAction`](#isasyncthunkaction) - accepts one or more action creators and returns true when all match
- [`isPending`](#ispending) - accepts one or more action creators and returns true when all match
- [`isFulfilled`](#isfulfilled) - accepts one or more action creators and returns true when all match
- [`isRejected`](#isrejected) - accepts one or more action creators and returns true when all match
- [`isRejectedWithValue`](#isrejectedwithvalue) - accepts one or more action creators and returns true when all match

## `isAllOf`

A higher-order function that accepts one or more of:

- `redux-toolkit` action creator functions such as the ones produced by:
  - [`createAction`](./createAction.mdx)
  - [`createSlice`](./createSlice.mdx#return-value)
  - [`createAsyncThunk`](./createAsyncThunk.mdx#promise-lifecycle-actions)
- type guard functions
- custom action creator functions that have a `.match` property that is a type guard

It will return a type guard function that returns `true` if _all_ of the provided functions match.

## `isAnyOf`

Accepts the same inputs as `isAllOf` and will return a type guard function that returns `true` if at least one of the provided functions match.

## `isAsyncThunkAction`

A higher-order function that returns a type guard function that may be used to check whether an action was created by [`createAsyncThunk`](./createAsyncThunk.mdx).

```ts title="isAsyncThunkAction usage"
import { isAsyncThunkAction } from '@reduxjs/toolkit'
import type { UnknownAction } from '@reduxjs/toolkit'
import { requestThunk1, requestThunk2 } from '@virtual/matchers'

const isARequestAction = isAsyncThunkAction(requestThunk1, requestThunk2)

function handleRequestAction(action: UnknownAction) {
  if (isARequestAction(action)) {
    // action is an action dispatched by either `requestThunk1` or `requestThunk2`
  }
}
```

## `isPending`

A higher-order function that returns a type guard function that may be used to check whether an action is a 'pending' action creator from the `createAsyncThunk` promise lifecycle.

```ts title="isPending usage"
import { isPending } from '@reduxjs/toolkit'
import type { UnknownAction } from '@reduxjs/toolkit'
import { requestThunk1, requestThunk2 } from '@virtual/matchers'

const isAPendingAction = isPending(requestThunk1, requestThunk2)

function handlePendingAction(action: UnknownAction) {
  if (isAPendingAction(action)) {
    // action is a pending action dispatched by either `requestThunk1` or `requestThunk2`
  }
}
```

## `isFulfilled`

A higher-order function that returns a type guard function that may be used to check whether an action is a 'fulfilled'' action creator from the `createAsyncThunk` promise lifecycle.

```ts title="isFulfilled usage"
import { isFulfilled } from '@reduxjs/toolkit'
import type { UnknownAction } from '@reduxjs/toolkit'
import { requestThunk1, requestThunk2 } from '@virtual/matchers'

const isAFulfilledAction = isFulfilled(requestThunk1, requestThunk2)

function handleFulfilledAction(action: UnknownAction) {
  if (isAFulfilledAction(action)) {
    // action is a fulfilled action dispatched by either `requestThunk1` or `requestThunk2`
  }
}
```

## `isRejected`

A higher-order function that returns a type guard function that may be used to check whether an action is a 'rejected' action creator from the `createAsyncThunk` promise lifecycle.

```ts title="isRejected usage"
import { isRejected } from '@reduxjs/toolkit'
import type { UnknownAction } from '@reduxjs/toolkit'
import { requestThunk1, requestThunk2 } from '@virtual/matchers'

const isARejectedAction = isRejected(requestThunk1, requestThunk2)

function handleRejectedAction(action: UnknownAction) {
  if (isARejectedAction(action)) {
    // action is a rejected action dispatched by either `requestThunk1` or `requestThunk2`
  }
}
```

## `isRejectedWithValue`

A higher-order function that returns a type guard function that may be used to check whether an action is a 'rejected' action creator from the `createAsyncThunk` promise lifecycle that was created by [`rejectWithValue`](./createAsyncThunk.mdx#handling-thunk-errors).

```ts title="isRejectedWithValue usage"
import { isRejectedWithValue } from '@reduxjs/toolkit'
import type { UnknownAction } from '@reduxjs/toolkit'
import { requestThunk1, requestThunk2 } from '@virtual/matchers'

const isARejectedWithValueAction = isRejectedWithValue(
  requestThunk1,
  requestThunk2,
)

function handleRejectedWithValueAction(action: UnknownAction) {
  if (isARejectedWithValueAction(action)) {
    // action is a rejected action dispatched by either `requestThunk1` or `requestThunk2`
    // where rejectWithValue was used
  }
}
```

## Using matchers to reduce code complexity, duplication and boilerplate

When using the `builder` pattern to construct a reducer, we add cases or matchers one at a time. However, by using `isAnyOf` or `isAllOf`,
we're able to easily use the same matcher for several cases in a type-safe manner.

First, let's examine an unnecessarily complex example:

```ts title="Example without using a matcher utility"
import { createAsyncThunk, createReducer } from '@reduxjs/toolkit'
import type { PayloadAction } from '@reduxjs/toolkit'

interface Data {
  isInteresting: boolean
  isSpecial: boolean
}

interface Special extends Data {
  isSpecial: true
}

interface Interesting extends Data {
  isInteresting: true
}

function isSpecial(
  action: PayloadAction<Data>,
): action is PayloadAction<Special> {
  return action.payload.isSpecial
}

function isInteresting(
  action: PayloadAction<Data>,
): action is PayloadAction<Interesting> {
  return action.payload.isInteresting
}

interface ExampleState {
  isSpecial: boolean
  isInteresting: boolean
}

const initialState = {
  isSpecial: false,
  isInteresting: false,
} satisfies ExampleState as ExampleState

export const isSpecialAndInterestingThunk = createAsyncThunk(
  'isSpecialAndInterestingThunk',
  () => {
    return {
      isSpecial: true,
      isInteresting: true,
    }
  },
)

// This has unnecessary complexity
const loadingReducer = createReducer(initialState, (builder) => {
  builder.addCase(isSpecialAndInterestingThunk.fulfilled, (state, action) => {
    if (isSpecial(action)) {
      state.isSpecial = true
    }
    if (isInteresting(action)) {
      state.isInteresting = true
    }
  })
})
```

In this scenario, we can use `isAllOf` to simplify our code and reduce some of the boilerplate.

```ts title="Refactoring with isAllOf"
import { createReducer, isAllOf } from '@reduxjs/toolkit'
import {
  isSpecialAndInterestingThunk,
  initialState,
  isSpecial,
  isInteresting,
} from '@virtual/matchers' // This is a fake pkg that provides the types shown above
import type { Data } from '@virtual/matchers' // This is a fake pkg that provides the types shown above

const loadingReducer = createReducer(initialState, (builder) => {
  builder
    .addMatcher(
      isAllOf(isSpecialAndInterestingThunk.fulfilled, isSpecial),
      (state, action) => {
        state.isSpecial = true
      },
    )
    .addMatcher(
      isAllOf(isSpecialAndInterestingThunk.fulfilled, isInteresting),
      (state, action) => {
        state.isInteresting = true
      },
    )
})
```

## Using matchers as a TypeScript Type Guard

The function returned by `isAllOf` and `isAnyOf` can also be used as a TypeScript type guard in other contexts.

```ts title="Using isAllOf as a type guard"
import { isAllOf } from '@reduxjs/toolkit'
import type { PayloadAction } from '@reduxjs/toolkit'
import { isSpecial, isInteresting } from '@virtual/matchers' // This is a fake pkg that provides the types shown above
import type { Data } from '@virtual/matchers' // This is a fake pkg that provides the types shown above

const isSpecialAndInteresting = isAllOf(isSpecial, isInteresting)

function someFunction(action: PayloadAction<Data>) {
  if (isSpecialAndInteresting(action)) {
    // "action" will be correctly typed as:
    // `PayloadAction<Special> & PayloadAction<Interesting>`
  }
}
```

```ts title="Using isAnyOf as a type guard"
import { isAnyOf } from '@reduxjs/toolkit'
import type { PayloadAction } from '@reduxjs/toolkit'
import { Data, isSpecial, isInteresting } from '@virtual/matchers' // this is a fake pkg that provides the types shown above

const isSpecialOrInteresting = isAnyOf(isSpecial, isInteresting)

function someFunction(action: PayloadAction<Data>) {
  if (isSpecialOrInteresting(action)) {
    // "action" will be correctly typed as:
    // `PayloadAction<Special> | PayloadAction<Interesting>`
  }
}
```

## Example

<iframe
  src="https://codesandbox.io/embed/redux-toolkit-matchers-example-e765q?fontsize=14&hidenavigation=1&module=%2Fsrc%2Ffeatures%2Fcounter%2FcounterSlice.ts&theme=dark&runonclick=1"
  style={{
    width: '100%',
    height: '500px',
    border: 0,
    borderRadius: 4,
    overflow: 'hidden',
  }}
  title="redux-toolkit-matchers-example"
  allow="accelerometer; ambient-light-sensor; camera; encrypted-media; geolocation; gyroscope; hid; microphone; midi; payment; usb; vr; xr-spatial-tracking"
  sandbox="allow-forms allow-modals allow-popups allow-presentation allow-same-origin allow-scripts"
></iframe>

---

### Api OtherExports (`docs/api/otherExports.mdx`)

---
id: other-exports
title: Other Exports
sidebar_label: Other Exports
hide_title: true
---

&nbsp;

# Other Exports

Redux Toolkit exports some of its internal utilities, and re-exports additional functions from other dependencies as well.

### `nanoid`

An inlined copy of [`nanoid/nonsecure`](https://github.com/ai/nanoid). Generates a non-cryptographically-secure random ID string. `createAsyncThunk` uses this by default for request IDs. May also be useful for other cases as well.

```ts
import { nanoid } from '@reduxjs/toolkit'

console.log(nanoid())
// 'dgPXxUz_6fWIQBD8XmiSy'
```

### `miniSerializeError`

The default error serialization function used by `createAsyncThunk`, based on https://github.com/sindresorhus/serialize-error. If its argument is an object (such as an `Error` instance), it returns a plain JS `SerializedError` object that copies over any of the listed fields. Otherwise, it returns a stringified form of the value: `{ message: String(value) }`.

```ts no-transpile
export interface SerializedError {
  name?: string
  message?: string
  stack?: string
  code?: string
}

export function miniSerializeError(value: any): SerializedError {}
```

### `copyWithStructuralSharing`

A utility that will recursively merge two similar objects together, preserving existing references if the values appear to be the same. This is used internally to help ensure that re-fetched data keeps using the same references unless the new data has actually changed, to avoid unnecessary re-renders. Otherwise, every re-fetch would likely cause the entire dataset to be replaced and all consuming components to always re-render.

If either of the inputs are not plain JS objects or arrays, the new value is returned.

```ts no-transpile
export function copyWithStructuralSharing<T>(oldObj: any, newObj: T): T
export function copyWithStructuralSharing(oldObj: any, newObj: any): any {}
```

## Exports from Other Libraries

### `createNextState`

The default immutable update function from the [`immer` library](https://immerjs.github.io/immer/), re-exported here as `createNextState` (also commonly referred to as [`produce`](https://immerjs.github.io/immer/produce))

### `current`

[The `current` function](https://immerjs.github.io/immer/current) from the [`immer` library](https://immerjs.github.io/immer/), which takes a snapshot of the current state of a draft and finalizes it (but without freezing). Current is a great utility to print the current state during debugging, and the output of `current` can also be safely leaked outside the producer.

```ts
import { createReducer, createAction, current } from '@reduxjs/toolkit'

interface Todo {
  //...
}
const addTodo = createAction<Todo>('addTodo')

const initialState = [] satisfies Todo[] as Todo[]

const todosReducer = createReducer(initialState, (builder) => {
  builder.addCase(addTodo, (state, action) => {
    state.push(action.payload)
    console.log(current(state))
  })
})
```

### `original`

[The `original` function](https://immerjs.github.io/immer/original) from the [`immer` library](https://immerjs.github.io/immer/), which returns the original object. This is particularly useful for referential equality check in reducers.

### `isDraft`

[The `isDraft` function](https://immerjs.github.io/immer/original) from the [`immer` library](https://immerjs.github.io/immer/), which checks to see if a given value is a Proxy-wrapped "draft" state.

### `freeze`

[The `freeze` function](https://immerjs.github.io/immer/api) from the [`immer` library](https://immerjs.github.io/immer/), which [freezes](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze) draftable objects.

### `combineReducers`

Redux's [`combineReducers`](/api/combinereducers), re-exported for convenience. While `configureStore` calls this internally, you may wish to call it yourself to compose multiple levels of slice reducers.

### `compose`

Redux's [`compose`](/api/compose). It composes functions from right to left.
This is a functional programming utility. You might want to use it to apply several store custom enhancers/ functions in a row.

### `bindActionCreators`

Redux's [`bindActionCreators`](/api/bindactioncreators). It wraps action creators with `dispatch()` so that they dispatch immediately when called.

### `createStore`

Redux's [`createStore`](/api/createstore). You should not need to use this directly.

### `applyMiddleware`

Redux's [`applyMiddleware`](/api/applymiddleware). You should not need to use this directly.

---

### Api SerializabilityMiddleware (`docs/api/serializabilityMiddleware.mdx`)

---
id: serializabilityMiddleware
title: Serializability Middleware
sidebar_label: Serializability Middleware
hide_title: true
---

&nbsp;

# Serializability Middleware

A custom middleware that detects if any non-serializable values have been included in state or dispatched actions, modeled after `redux-immutable-state-invariant`. Any detected non-serializable values will be logged to the console.

This middleware is added to the store by default by [`configureStore`](./configureStore.mdx) and [`getDefaultMiddleware`](./getDefaultMiddleware.mdx).

You can customize the behavior of this middleware by passing any of the supported options as the `serializableCheck` value for `getDefaultMiddleware`.

## Options

```
/* Detailed source-code truncated for AI context efficiency. */
```

## Exports

### `createSerializableStateInvariantMiddleware`

Creates an instance of the serializability check middleware, with the given options.

You will most likely not need to call this yourself, as `getDefaultMiddleware` already does so.

Example:

```ts
// file: reducer.ts noEmit

export default function (state = {}, action: any) {
  return state
}

// file: store.ts

import { Iterable } from 'immutable'
import {
  configureStore,
  createSerializableStateInvariantMiddleware,
  isPlain,
  Tuple,
} from '@reduxjs/toolkit'
import reducer from './reducer'

// Augment middleware to consider Immutable.JS iterables serializable
const isSerializable = (value: any) =>
  Iterable.isIterable(value) || isPlain(value)

const getEntries = (value: any) =>
  Iterable.isIterable(value) ? value.entries() : Object.entries(value)

const serializableMiddleware = createSerializableStateInvariantMiddleware({
  isSerializable,
  getEntries,
})

const store = configureStore({
  reducer,
  middleware: () => new Tuple(serializableMiddleware),
})
```

### `isPlain`

Checks whether the given value is considered a "plain value" or not.

Currently implemented as:

```ts
// file: src/isPlainObject.ts noEmit

declare function isPlainObject(value: unknown): value is object
export default isPlainObject

// file: src/serializableStateInvariantMiddleware.ts
import isPlainObject from './isPlainObject'

export function isPlain(val: any) {
  return (
    typeof val === 'undefined' ||
    val === null ||
    typeof val === 'string' ||
    typeof val === 'boolean' ||
    typeof val === 'number' ||
    Array.isArray(val) ||
    isPlainObject(val)
  )
}
```

This will accept all standard JS objects, arrays, and primitives, but return false for `Date`s, `Map`s, and other similar class instances.

---

### Rtk Query Comparison (`docs/rtk-query/comparison.md`)

---
id: comparison
title: Comparison with Other Tools
sidebar_label: Comparison with Other Tools
hide_title: true
description: 'RTK Query > Comparison: Compares features and tradeoffs vs other similar tools'
---

&nbsp;

# Comparison with Other Tools

**RTK Query takes inspiration from many other data fetching libraries in the ecosystem**. Much like [the Redux core library was inspired by tools like Flux and Elm](/understanding/history-and-design/prior-art), RTK Query builds on API design patterns and feature concepts popularized by libraries like [React Query](https://react-query.tanstack.com/), [SWR](https://swr.vercel.app/), [Apollo](https://www.apollographql.com/), and [Urql](https://formidable.com/open-source/urql/). RTK Query has been written from scratch, but tries to use the best concepts from those libraries and other data fetching tools, with an eye towards leveraging the unique strengths and capabilities of Redux.

We think that all of those tools are great! If you're using one of them, you're happy with it, and it solves the problems you are facing in your app, keep using that tool. The information on this page is meant to help show **where there are differences in features, implementation approaches, and API design**. The goal is to help you **make informed decisions and understand tradeoffs**, rather than argue that tool X is better than tool Y.

## When Should You Use RTK Query?

In general, the main reasons to use RTK Query are:

- You already have a Redux app and you want to simplify your existing data fetching logic
- You want to be able to use the Redux DevTools to see the history of changes to your state over time
- You want to be able to integrate the RTK Query behavior with the rest of the Redux ecosystem
- Your app logic needs to work outside of React

### Unique Capabilities

RTK Query has some unique API design aspects and capabilities that are worth considering.

- With React Query and SWR, you usually define your hooks yourself, and you can do that all over the place and on the fly. With RTK Query, you do so in one central place by defining an "API slice" with multiple endpoints ahead of time. This allows for a more tightly integrated model of mutations automatically invalidating/refetching queries on trigger.
- Because RTK Query dispatches normal Redux actions as requests are processed, all actions are visible in the Redux DevTools. Additionally, every request is automatically visible to your Redux reducers and can easily update the global application state if necessary ([see example](https://github.com/reduxjs/redux-toolkit/issues/958#issuecomment-809570419)). You can use the endpoint [matcher functionality](./api/created-api/endpoints#matchers) to do additional processing of cache-related actions in your own reducers.
- Like Redux itself, the main RTK Query functionality is UI-agnostic and can be used with any UI layer
- You can easily invalidate entities or patch existing query data (via `util.updateQueryData`) from middleware.
- RTK Query enables [streaming cache updates](./usage/streaming-updates.mdx), such as updating the initial fetched data as messages are received over a websocket, and has built in support for [optimistic updates](./usage/manual-cache-updates.mdx#optimistic-updates) as well.
- RTK Query ships a very tiny and flexible fetch wrapper: [`fetchBaseQuery`](./api/fetchBaseQuery.mdx). It's also very easy to [swap our client with your own](./usage/customizing-queries.mdx), such as using `axios`, `redaxios`, or something custom.
- RTK Query has [a (currently experimental) code-gen tool](https://github.com/reduxjs/redux-toolkit/tree/master/packages/rtk-query-codegen-openapi) that will take an OpenAPI spec or GraphQL schema and give you a typed API client, as well as provide methods for enhancing the generated client after the fact.

## Tradeoffs

### No Normalized or Deduplicated Cache

RTK Query deliberately **does _not_ implement a cache that would deduplicate identical items across multiple requests**. There are several reasons for this:

- A fully normalized shared-across-queries cache is a _hard_ problem to solve
- We don't have the time, resources, or interest in trying to solve that right now
- In many cases, simply refetching data when it's invalidated works well and is easier to understand
- At a minimum, RTKQ can help solve the general use case of "fetch some data", which is a big pain point for a lot of people

### Bundle Size

RTK Query adds a fixed one-time amount to your app's bundle size. Since RTK Query builds on top of Redux Toolkit and React-Redux, the added size varies depending on whether you are already using those in your app. The estimated min+gzip bundle sizes are:

- If you are using RTK already: ~9kb for RTK Query and ~2kb for the hooks.
- If you are not using RTK already:
  - Without React: 17 kB for RTK+dependencies+RTK Query
  - With React: 19kB + React-Redux, which is a peer dependency

Adding additional endpoint definitions should only increase size based on the actual code inside the `endpoints` definitions, which will typically be just a few bytes.

The functionality included in RTK Query quickly pays for the added bundle size, and the elimination of hand-written data fetching logic should be a net improvement in size for most meaningful applications.

## Comparing Feature Sets

It's worth comparing the feature sets of all these tools to get a sense of their similarities and differences.

:::info

This comparison table strives to be as accurate and as unbiased as possible. If you use any of these libraries and feel the information could be improved, feel free to suggest changes (with notes or evidence of claims) by [opening an issue](https://github.com/reduxjs/redux-toolkit/issues/new).

:::

| Feature                                | rtk-query                               | [react-query]            | [apollo]                                                                            | [urql]                                                                                                      |
| -------------------------------------- | --------------------------------------- | ------------------------ | ----------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------- |
| **Supported Protocols**                | any, REST included                      | any, none included       | GraphQL                                                                             | GraphQL                                                                                                     |
| **API Definition**                     | declarative                             | on use, declarative      | GraphQL schema                                                                      | GraphQL schema                                                                                              |
| **Cache by**                           | endpoint + serialized arguments         | user-defined query-key   | type/id                                                                             | type/id?                                                                                                    |
| **Invalidation Strategy + Refetching** | declarative, by type and/or type/id     | manual by cache key      | automatic cache updates on per-entity level, manual query invalidation by cache key | declarative, by type OR automatic cache updates on per-entity level, manual query invalidation by cache key |
| **Polling**                            | yes                                     | yes                      | yes                                                                                 | yes                                                                                                         |
| **Parallel queries**                   | yes                                     | yes                      | yes                                                                                 | yes                                                                                                         |
| **Dependent queries**                  | yes                                     | yes                      | yes                                                                                 | yes                                                                                                         |
| **Skip queries**                       | yes                                     | yes                      | yes                                                                                 | yes                                                                                                         |
| **Lagged queries**                     | yes                                     | yes                      | no                                                                                  | ?                                                                                                           |
| **Auto garbage collection**            | yes                                     | yes                      | no                                                                                  | ?                                                                                                           |
| **Normalized caching**                 | no                                      | no                       | yes                                                                                 | yes                                                                                                         |
| **Infinite scrolling**                 | yes                                     | yes                      | requires manual code                                                                | ?                                                                                                           |
| **Prefetching**                        | yes                                     | yes                      | yes                                                                                 | yes?                                                                                                        |
| **Retrying**                           | yes                                     | yes                      | requires manual code                                                                | ?                                                                                                           |
| **Optimistic updates**                 | can update cache by hand                | can update cache by hand | `optimisticResponse`                                                                | ?                                                                                                           |
| **Manual cache manipulation**          | yes                                     | yes                      | yes                                                                                 | yes                                                                                                         |
| **Platforms**                          | hooks for React, everywhere Redux works | hooks for React          | various                                                                             | various                                                                                                     |

[react-query]: https://react-query.tanstack.com/
[apollo]: https://www.apollographql.com/
[urql]: https://formidable.com/open-source/urql/

## Further Information

- The [React Query "Comparison" page](https://tanstack.com/query/latest/docs/react/comparison) has an additional detailed feature set comparison table and discussion of capabilities
- Urql maintainer Phil Pluckthun wrote [an excellent explanation of what a "normalized cache" is and how Urql's cache works](https://kitten.sh/graphql-normalized-caching)
- The [RTK Query "Cache Behavior" page](./usage/cache-behavior.mdx#tradeoffs) has further details on why RTK Query does not implement a normalized cache

---

### Rtk Query Usage With Typescript (`docs/rtk-query/usage-with-typescript.mdx`)

---
id: usage-with-typescript
title: Usage With TypeScript
sidebar_label: Usage With TypeScript
hide_title: true
description: 'RTK Query > TypeScript: Instructions on how to use RTK Query with TypeScript'
---

&nbsp;

# Usage With TypeScript

:::tip What You'll Learn

- Details on how to use various RTK Query APIs with TypeScript

:::

## Introduction

As with the rest of the Redux Toolkit package, RTK Query is written in TypeScript, and its API is designed for seamless use in TypeScript applications.

This page provides details for using APIs included in RTK Query with TypeScript and how to type them correctly.

:::info

RTK Query [supports TS versions released within the last 2 years](../usage/usage-with-typescript.md).

If you encounter any problems with the types that are not described on this page, please [open an issue](https://github.com/reduxjs/redux-toolkit/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-desc) for discussion.

:::

## `createApi`

### Using auto-generated React Hooks

The React-specific entry point for RTK Query exports a version of [`createApi`](./api/createApi.mdx) which automatically generates React hooks for each of the defined query & mutation [`endpoints`](./api/createApi.mdx#endpoints).

To use the auto-generated React Hooks as a TypeScript user, **you'll need to use TS4.1+**.

```ts
// file: src/services/types.ts noEmit
export type Pokemon = {}

// file: src/services/pokemon.ts
// Need to use the React-specific entry point to allow generating React hooks
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'
import type { Pokemon } from './types'

// Define a service using a base URL and expected endpoints
export const pokemonApi = createApi({
  reducerPath: 'pokemonApi',
  baseQuery: fetchBaseQuery({ baseUrl: 'https://pokeapi.co/api/v2/' }),
  endpoints: (build) => ({
    getPokemonByName: build.query<Pokemon, string>({
      query: (name) => `pokemon/${name}`,
    }),
  }),
})

// highlight-start
// Export hooks for usage in function components, which are
// auto-generated based on the defined endpoints
export const { useGetPokemonByNameQuery } = pokemonApi
// highlight-end
```

For older versions of TS, you can use `api.endpoints.[endpointName].useQuery/useMutation` to access the same hooks.

```ts title="Accessing api hooks directly"
// file: src/services/types.ts noEmit
export type Pokemon = {}

// file: src/services/pokemon.ts noEmit
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'
import type { Pokemon } from './types'

export const pokemonApi = createApi({
  reducerPath: 'pokemonApi',
  baseQuery: fetchBaseQuery({ baseUrl: 'https://pokeapi.co/api/v2/' }),
  endpoints: (build) => ({
    getPokemonByName: build.query<Pokemon, string>({
      query: (name) => `pokemon/${name}`,
    }),
  }),
})

export const { useGetPokemonByNameQuery } = pokemonApi

// file: src/services/manual-query.ts
import { pokemonApi } from './pokemon'

const useGetPokemonByNameQuery = pokemonApi.endpoints.getPokemonByName.useQuery
```

### Typing a `baseQuery`

Typing a custom [`baseQuery`](./api/createApi.mdx#basequery) can be done using the `BaseQueryFn` type exported by RTK Query.

```ts title="Base Query signature" no-transpile
export type BaseQueryFn<
  Args = any,
  Result = unknown,
  Error = unknown,
  DefinitionExtraOptions = {},
  Meta = {},
> = (
  args: Args,
  api: BaseQueryApi,
  extraOptions: DefinitionExtraOptions,
) => MaybePromise<QueryReturnValue<Result, Error, Meta>>

export interface BaseQueryApi {
  signal: AbortSignal
  dispatch: ThunkDispatch<any, any, any>
  getState: () => unknown
}

export type QueryReturnValue<T = unknown, E = unknown, M = unknown> =
  | {
      error: E
      data?: undefined
      meta?: M
    }
  | {
      error?: undefined
      data: T
      meta?: M
    }
```

The `BaseQueryFn` type accepts the following generics:

- `Args` - The type for the first parameter of the function. The result returned by a [`query`](./api/createApi.mdx#query) property on an endpoint will be passed here.
- `Result` - The type to be returned in the `data` property for the success case. Unless you expect all queries and mutations to return the same type, it is recommended to keep this typed as `unknown`, and specify the types individually as shown [below](#typing-query-and-mutation-endpoints).
- `Error` - The type to be returned for the `error` property in the error case. This type also applies to all [`queryFn`](#typing-a-queryfn) functions used in endpoints throughout the API definition.
- `DefinitionExtraOptions` - The type for the third parameter of the function. The value provided to the [`extraOptions`](./api/createApi.mdx#extraoptions) property on an endpoint will be passed here.
- `Meta` - the type of the `meta` property that may be returned from calling the `baseQuery`. The `meta` property is accessible as the second argument to [`transformResponse`](./api/createApi.mdx#transformresponse) and [`transformErrorResponse`](./api/createApi.mdx#transformerrorresponse).

:::note

The `meta` property returned from a `baseQuery` will always be considered as potentially undefined, as a `throw` in the error case may result in it not being provided. When accessing values from the `meta` property, this should be accounted for, e.g. using [optional chaining](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Optional_chaining)

:::

```ts title="Simple baseQuery TypeScript example"
import { createApi } from '@reduxjs/toolkit/query'
import type { BaseQueryFn } from '@reduxjs/toolkit/query'

const simpleBaseQuery: BaseQueryFn<
  string, // Args
  unknown, // Result
  { reason: string }, // Error
  { shout?: boolean }, // DefinitionExtraOptions
  { timestamp: number } // Meta
> = (arg, api, extraOptions) => {
  // `arg` has the type `string`
  // `api` has the type `BaseQueryApi` (not configurable)
  // `extraOptions` has the type `{ shout?: boolean }

  const meta = { timestamp: Date.now() }

  if (arg === 'forceFail') {
    return {
      error: {
        reason: 'Intentionally requested to fail!',
        meta,
      },
    }
  }

  if (extraOptions.shout) {
    return { data: 'CONGRATULATIONS', meta }
  }

  return { data: 'congratulations', meta }
}

const api = createApi({
  baseQuery: simpleBaseQuery,
  endpoints: (build) => ({
    getSupport: build.query({
      query: () => 'support me',
      extraOptions: {
        shout: true,
      },
    }),
  }),
})
```

### Typing query and mutation `endpoints`

`endpoints` for an api are defined as an object using the builder syntax. Both `query` and `mutation` endpoints can be typed by providing types to the generics in `<ResultType, QueryArg>` format.

- `ResultType` - The type of the final data returned by the query, factoring an optional [`transformResponse`](./api/createApi.mdx#transformresponse).
  - If `transformResponse` is not provided, then it is treated as though a successful query will return this type instead.
  - If `transformResponse` _is_ provided, the input type for `transformResponse` must also be specified, to indicate the type that the initial query returns. The return type for `transformResponse` must match `ResultType`.
  - If `queryFn` is used rather than `query`, then it must return the following shape for the success case:
    ```ts no-transpile
    {
      data: ResultType
    }
    ```
- `QueryArg` - The type of the input that will be passed as the only parameter to the `query` property of the endpoint, or the first parameter of a `queryFn` property if used instead.
  - If `query` doesn't have a parameter, then `void` type has to be provided explicitly.
  - If `query` has an optional parameter, then a union type with the type of parameter, and `void` has to be provided, e.g. `number | void`.

```ts title="Defining endpoints with TypeScript"
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'
interface Post {
  id: number
  name: string
}

const api = createApi({
  baseQuery: fetchBaseQuery({ baseUrl: '/' }),
  endpoints: (build) => ({
    // highlight-start
    //              ResultType  QueryArg
    //                    v       v
    getPost: build.query<Post, number>({
      // inferred as `number` from the `QueryArg` type
      //       v
      query: (id) => `post/${id}`,
      // An explicit type must be provided to the raw result that the query returns
      // when using `transformResponse`
      //                             v
      transformResponse: (rawResult: { result: { post: Post } }, meta) => {
        //                                                        ^
        // The optional `meta` property is available based on the type for the `baseQuery` used

        // The return value for `transformResponse` must match `ResultType`
        return rawResult.result.post
      },
    }),
    // highlight-end
  }),
})
```

:::note

`queries` and `mutations` can also have their return type defined by a [`baseQuery`](#typing-a-basequery) rather than the method shown above, however, unless you expect all of your queries and mutations to return the same type, it is recommended to leave the return type of the `baseQuery` as `unknown`.

:::

### Typing a `queryFn`

As mentioned in [Typing query and mutation endpoints](#typing-query-and-mutation-endpoints), a `queryFn` will receive its result & arg types from the generics provided to the corresponding built endpoint.

```ts
// file: randomData.ts noEmit
export declare const getRandomName: () => string

// file: api.ts
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'
import { getRandomName } from './randomData'

interface Post {
  id: number
  name: string
}

const api = createApi({
  baseQuery: fetchBaseQuery({ baseUrl: '/' }),
  endpoints: (build) => ({
    // highlight-start
    //              ResultType  QueryArg
    //                    v       v
    getPost: build.query<Post, number>({
      // inferred as `number` from the `QueryArg` type
      //         v
      queryFn: (arg, queryApi, extraOptions, baseQuery) => {
        const post: Post = {
          id: arg,
          name: getRandomName(),
        }
        // For the success case, the return type for the `data` property
        // must match `ResultType`
        //              v
        return { data: post }
      },
    }),
    // highlight-end
  }),
})
```

The error type that a `queryFn` must return is determined by the [`baseQuery`](#typing-a-basequery) provided to `createApi`.

With [`fetchBaseQuery`](./api/fetchBaseQuery.mdx), the error type is like so:

```ts title="fetchBaseQuery error shape" no-transpile
{
  status: number
  data: any
}
```

An error case for the example above using `queryFn` and the error type from `fetchBaseQuery` could look like:

```ts title="queryFn error example with error type from fetchBaseQuery"
// file: randomData.ts noEmit
export declare const getRandomName: () => string

// file: api.ts
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'
import { getRandomName } from './randomData'

interface Post {
  id: number
  name: string
}

const api = createApi({
  baseQuery: fetchBaseQuery({ baseUrl: '/' }),
  endpoints: (build) => ({
    // highlight-start
    getPost: build.query<Post, number>({
      queryFn: (arg, queryApi, extraOptions, baseQuery) => {
        // highlight-start
        if (arg <= 0) {
          return {
            error: {
              status: 500,
              statusText: 'Internal Server Error',
              data: 'Invalid ID provided.',
            },
          }
        }
        // highlight-end
        const post: Post = {
          id: arg,
          name: getRandomName(),
        }
        return { data: post }
      },
    }),
  }),
})
```

For users who wish to _only_ use `queryFn` for each endpoint and not include a `baseQuery` at all, RTK Query provides a `fakeBaseQuery` function that can be used to easily specify the error type each `queryFn` should return.

```ts title="Excluding baseQuery for all endpoints"
import { createApi, fakeBaseQuery } from '@reduxjs/toolkit/query'

// highlight-start
type CustomErrorType = { reason: 'too cold' | 'too hot' }
// highlight-end

const api = createApi({
  // highlight-start
  // This type will be used as the error type for all `queryFn` functions provided
  //                              v
  baseQuery: fakeBaseQuery<CustomErrorType>(),
  // highlight-end
  endpoints: (build) => ({
    eatPorridge: build.query<'just right', 1 | 2 | 3>({
      // highlight-start
      queryFn(seat) {
        if (seat === 1) {
          return { error: { reason: 'too cold' } }
        }

        if (seat === 2) {
          return { error: { reason: 'too hot' } }
        }

        return { data: 'just right' }
      },
      // highlight-end
    }),
    microwaveHotPocket: build.query<'delicious!', number>({
      // highlight-start
      queryFn(duration) {
        if (duration < 110) {
          return { error: { reason: 'too cold' } }
        }
        if (duration > 140) {
          return { error: { reason: 'too hot' } }
        }

        return { data: 'delicious!' }
      },
      // highlight-end
    }),
  }),
})
```

### Typing `dispatch` and `getState`

`createApi` exposes the standard Redux `dispatch` and `getState` methods in several places, such as the `lifecycleApi` argument in lifecycle methods, or the `baseQueryApi` argument passed to `queryFn` methods and base query functions.

Normally, [your application infers `RootState` and `AppDispatch` types from the store setup](/tutorials/quick-start#define-root-state-and-dispatch-types). Since `createApi` has to be called prior to creating the Redux store and is used as part of the store setup sequence, it can't directly know or use those types - it would cause a circular type inference error.

By default, `dispatch` usages inside of `createApi` will be typed as `ThunkDispatch`, and `getState` usages are typed as `() => unknown`. You will need to assert the type when needed - `getState() as RootState`. You may also include an explicit return type for the function as well, in order to break the circular type inference cycle:

```ts no-transpile
const api = createApi({
  baseQuery,
  endpoints: (build) => ({
    getTodos: build.query<Todo[], void>({
      async queryFn() {
        // highlight-start
        // Cast state as `RootState`
        const state = getState() as RootState
        // highlight-end
        const text = state.todoTexts[queryFnCalls]
        return { data: [{ id: `${queryFnCalls++}`, text }] }
      },
    }),
  }),
})
```

### Typing `providesTags`/`invalidatesTags`

RTK Query utilizes a cache tag invalidation system in order to provide [automated re-fetching](./usage/automated-refetching.mdx) of stale data.

When using the function notation, both the `providesTags` and `invalidatesTags` properties on endpoints are called with the following arguments:

- result: `ResultType` | `undefined` - The result returned by a successful query. The type corresponds with `ResultType` as [supplied to the built endpoint](#typing-query-and-mutation-endpoints). In the error case for a query, this will be `undefined`.
- error: `ErrorType` | `undefined` - The error returned by an errored query. The type corresponds with `Error` as [supplied to the `baseQuery` for the api](#typing-a-basequery). In the success case for a query, this will be `undefined`.
- arg: `QueryArg` - The argument supplied to the `query` property when the query itself is called. The type corresponds with `QueryArg` as [supplied to the built endpoint](#typing-query-and-mutation-endpoints).

A recommended use-case with `providesTags` when a query returns a list of items is to provide a tag for each item in the list using the entity ID, as well as a 'LIST' ID tag (see [Advanced Invalidation with abstract tag IDs](./usage/automated-refetching.mdx#advanced-invalidation-with-abstract-tag-ids)).

This is often written by spreading the result of mapping the received data into an array, as well as an additional item in the array for the `'LIST'` ID tag. When spreading the mapped array, by default, TypeScript will broaden the `type` property to `string`. As the tag `type` must correspond to one of the string literals provided to the [`tagTypes`](./api/createApi.mdx#tagtypes) property of the api, the broad `string` type will not satisfy TypeScript. In order to alleviate this, the tag `type` can be cast `as const` to prevent the type being broadened to `string`.

```ts title="providesTags TypeScript example"
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'
interface Post {
  id: number
  name: string
}
type PostsResponse = Post[]

const api = createApi({
  baseQuery: fetchBaseQuery({ baseUrl: '/' }),
  tagTypes: ['Posts'],
  endpoints: (build) => ({
    getPosts: build.query<PostsResponse, void>({
      query: () => 'posts',
      providesTags: (result) =>
        result
          ? [
              // highlight-start
              ...result.map(({ id }) => ({ type: 'Posts' as const, id })),
              { type: 'Posts', id: 'LIST' },
              // highlight-end
            ]
          : [{ type: 'Posts', id: 'LIST' }],
    }),
  }),
})
```



## Skipping queries with TypeScript using `skipToken`



RTK Query provides the ability to conditionally skip queries from automatically running using the `skip` parameter as part of query hook options (see [Conditional Fetching](./usage/conditional-fetching.mdx)).

TypeScript users may find that they encounter invalid type scenarios when a query argument is typed to not be `undefined`, and they attempt to `skip` the query when an argument would not be valid.

```ts title="API definition"
// file: types.ts noEmit
export interface Post {
  id: number
  name: string
}

// file: api.ts
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'
import type { Post } from './types'

export const api = createApi({
  baseQuery: fetchBaseQuery({ baseUrl: '/' }),
  endpoints: (build) => ({
    // Query argument is required to be `number`, and can't be `undefined`
    //                            V
    getPost: build.query<Post, number>({
      query: (id) => `post/${id}`,
    }),
  }),
})

export const { useGetPostQuery } = api
```

```tsx no-transpile title="Using skip in a component"
import { useGetPostQuery } from './api'

function MaybePost({ id }: { id?: number }) {
  // This will produce a typescript error:
  // Argument of type 'number | undefined' is not assignable to parameter of type 'number | unique symbol'.
  // Type 'undefined' is not assignable to type 'number | unique symbol'.

  // @ts-expect-error id passed must be a number, but we don't call it when it isn't a number
  const { data } = useGetPostQuery(id, { skip: !id })

  return <div>...</div>
}
```

While you might be able to convince yourself that the query won't be called unless the `id` arg is a `number` at the time, TypeScript won't be convinced so easily.

RTK Query provides a `skipToken` export which can be used as an alternative to the `skip` option in order to skip queries, while remaining type-safe. When `skipToken` is passed as the query argument to `useQuery`, `useQueryState` or `useQuerySubscription`, it provides the same effect as setting `skip: true` in the query options, while also being a valid argument in scenarios where the `arg` might be undefined otherwise.

```tsx no-transpile title="Using skipToken in a component"
import { skipToken } from '@reduxjs/toolkit/query/react'
import { useGetPostQuery } from './api'

function MaybePost({ id }: { id?: number }) {
  // When `id` is nullish, we will still skip the query.
  // TypeScript is also happy that the query will only ever be called with a `number` now
  const { data } = useGetPostQuery(id ?? skipToken)

  return <div>...</div>
}
```

## Type safe error handling

When an error is gracefully provided from a [`base query`](./api/createApi.mdx#basequery), RTK query will provide the error
directly. If an unexpected error is thrown by user code rather than a handled error,
that error will be transformed into a `SerializedError` shape. Users should make sure that they are checking which kind of error they are dealing with before attempting to access its properties. This can be done in a type safe manner either
by using a type guard, e.g. by checking for [discriminated properties](https://www.typescriptlang.org/docs/handbook/2/narrowing.html#the-in-operator-narrowing),
or using a [type predicate](https://www.typescriptlang.org/docs/handbook/2/narrowing.html#using-type-predicates).

When using [`fetchBaseQuery`](./api/fetchBaseQuery.mdx), as your base query,
errors will be of type `FetchBaseQueryError | SerializedError`. The specific shapes of those types can be seen below.

```ts title="FetchBaseQueryError type"
export type FetchBaseQueryError =
  | {
      /**
       * * `number`:
       *   HTTP status code
       */
      status: number
      data: unknown
    }
  | {
      /**
       * * `"FETCH_ERROR"`:
       *   An error that occurred during execution of `fetch` or the `fetchFn` callback option
       **/
      status: 'FETCH_ERROR'
      data?: undefined
      error: string
    }
  | {
      /**
       * * `"PARSING_ERROR"`:
       *   An error happened during parsing.
       *   Most likely a non-JSON-response was returned with the default `responseHandler` "JSON",
       *   or an error occurred while executing a custom `responseHandler`.
       **/
      status: 'PARSING_ERROR'
      originalStatus: number
      data: string
      error: string
    }
  | {
      /**
       * * `"CUSTOM_ERROR"`:
       *   A custom error type that you can return from your `queryFn` where another error might not make sense.
       **/
      status: 'CUSTOM_ERROR'
      data?: unknown
      error: string
    }
```

```ts title="SerializedError type"
export interface SerializedError {
  name?: string
  message?: string
  stack?: string
  code?: string
}
```

### Error result example

When using `fetchBaseQuery`, the `error` property returned from a hook will have the type `FetchBaseQueryError | SerializedError | undefined`.
If an error is present, you can access error properties after narrowing the type to either `FetchBaseQueryError` or `SerializedError`.

```tsx no-transpile
import { usePostsQuery } from './services/api'

function PostDetail() {
  const { data, error, isLoading } = usePostsQuery()

  if (isLoading) {
    return <div>Loading...</div>
  }

  if (error) {
    if ('status' in error) {
      // you can access all properties of `FetchBaseQueryError` here
      const errMsg = 'error' in error ? error.error : JSON.stringify(error.data)

      return (
        <div>
          <div>An error has occurred:</div>
          <div>{errMsg}</div>
        </div>
      )
    }
    // you can access all properties of `SerializedError` here
    return <div>{error.message}</div>
  }

  if (data) {
    return (
      <div>
        {data.map((post) => (
          <div key={post.id}>Name: {post.name}</div>
        ))}
      </div>
    )
  }

  return null
}
```

### Inline error handling example

When handling errors inline after [`unwrapping`](../api/createAsyncThunk.mdx#unwrapping-result-actions) a mutation call,
a thrown error will have a type of `any` for typescript versions below 4.4,
or [`unknown` for versions 4.4+](https://devblogs.microsoft.com/typescript/announcing-typescript-4-4/#use-unknown-catch-variables).
In order to safely access properties of the error, you must first narrow the type to a known type.
This can be done using a [type predicate](https://www.typescriptlang.org/docs/handbook/2/narrowing.html#using-type-predicates)
as shown below.

```tsx no-transpile title="services/helpers.ts"
import { FetchBaseQueryError } from '@reduxjs/toolkit/query'

/**
 * Type predicate to narrow an unknown error to `FetchBaseQueryError`
 */
export function isFetchBaseQueryError(
  error: unknown,
): error is FetchBaseQueryError {
  return typeof error === 'object' && error != null && 'status' in error
}

/**
 * Type predicate to narrow an unknown error to an object with a string 'message' property
 */
export function isErrorWithMessage(
  error: unknown,
): error is { message: string } {
  return (
    typeof error === 'object' &&
    error != null &&
    'message' in error &&
    typeof (error as any).message === 'string'
  )
}
```

```tsx no-transpile title="addPost.tsx"
import { useState } from 'react'
import { useSnackbar } from 'notistack'
import { api } from './services/api'
import { isFetchBaseQueryError, isErrorWithMessage } from './services/helpers'

function AddPost() {
  const { enqueueSnackbar, closeSnackbar } = useSnackbar()
  const [name, setName] = useState('')
  const [addPost] = useAddPostMutation()

  async function handleAddPost() {
    try {
      await addPost(name).unwrap()
      setName('')
    } catch (err) {
      if (isFetchBaseQueryError(err)) {
        // you can access all properties of `FetchBaseQueryError` here
        const errMsg = 'error' in err ? err.error : JSON.stringify(err.data)
        enqueueSnackbar(errMsg, { variant: 'error' })
      } else if (isErrorWithMessage(err)) {
        // you can access a string 'message' property here
        enqueueSnackbar(err.message, { variant: 'error' })
      }
    }
  }

  return (
    <div>
      <input value={name} onChange={(e) => setName(e.target.value)} />
      <button>Add post</button>
    </div>
  )
}
```

## Schema Validation

Endpoints can have schemas for runtime validation of query args, responses, and errors. Any [Standard Schema](https://standardschema.dev/) compliant library can be used. See [API reference](./api/createApi.mdx#schema-validation) for full list of available schemas.

When following the default approach of explicitly specifying type parameters for queries and mutations, the schemas will be required to match the types provided.

```ts title="Explicitly typed endpoint" no-transpile
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'
import * as v from 'valibot'

const postSchema = v.object({
  id: v.number(),
  name: v.string(),
})
type Post = v.InferOutput<typeof postSchema>

const api = createApi({
  baseQuery: fetchBaseQuery({ baseUrl: '/' }),
  endpoints: (build) => ({
    getPost: build.query<Post, { id: number }>({
      query: ({ id }) => `/post/${id}`,
      responseSchema: postSchema, // errors if type mismatch
    }),
  }),
})
```

Schemas can also be used as a source of inference, meaning that the type parameters can be omitted.

```ts title="Implicitly typed endpoint" no-transpile
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'
import * as v from 'valibot'

const postSchema = v.object({
  id: v.number(),
  name: v.string(),
})
type Post = v.InferOutput<typeof postSchema>

const api = createApi({
  baseQuery: fetchBaseQuery({ baseUrl: '/' }),
  endpoints: (build) => ({
    getPost: build.query({
      // infer arg from here
      query: ({ id }: { id: number }) => `/post/${id}`,
      // infer result from here
      responseSchema: postSchema,
    }),
    getTransformedPost: build.query({
      // infer arg from here
      query: ({ id }: { id: number }) => `/post/${id}`,
      // infer untransformed result from here
      rawResponseSchema: postSchema,
      // infer transformed result from here
      transformResponse: (response) => ({
        ...response,
        published_at: new Date(response.published_at),
      }),
    }),
  }),
})
```

:::warning

Schemas should _not_ perform any transformation that would change the type of the value.

```ts title="Incorrect usage" no-transpile
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'
import * as v from 'valibot'
import { titleCase } from 'lodash'

const postSchema = v.object({
  id: v.number(),
  name: v.pipe(
    v.string(),
    v.transform(titleCase), // fine - string -> string
  ),
  published_at: v.pipe(
    v.string(),
    // highlight-next-line
    v.transform((s) => new Date(s)), // not allowed!
    v.date(),
  ),
})
type Post = v.InferOutput<typeof postSchema>

const api = createApi({
  baseQuery: fetchBaseQuery({ baseUrl: '/' }),
  endpoints: (build) => ({
    getPost: build.query<Post, { id: number }>({
      query: ({ id }) => `/post/${id}`,
      responseSchema: postSchema,
    }),
  }),
})
```

Instead, transformation should be done with `transformResponse` and `transformErrorResponse` (when using `query`) or inside `queryFn` (when using `queryFn`).

```ts title="Correct usage" no-transpile
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'
import * as v from 'valibot'

const postSchema = v.object({
  id: v.number(),
  name: v.string(),
  published_at: v.string(),
})
type RawPost = v.InferOutput<typeof postSchema>
type Post = Omit<RawPost, 'published_at'> & { published_at: Date }

const api = createApi({
  baseQuery: fetchBaseQuery({ baseUrl: '/' }),
  endpoints: (build) => ({
    getPost: build.query<Post, { id: number }>({
      query: ({ id }) => `/post/${id}`,
      // use rawResponseSchema to validate *before* transformation
      rawResponseSchema: postSchema,
      // highlight-start
      transformResponse: (response) => ({
        ...response,
        published_at: new Date(response.published_at),
      }),
      // highlight-end
    }),
  }),
})
```

:::

---

### Tutorials Rtk Query (`docs/tutorials/rtk-query.mdx`)

---
id: rtk-query
title: RTK Query
sidebar_label: RTK Query Quick Start
hide_title: true
---

&nbsp;

# RTK Query Quick Start

:::tip What You'll Learn

- How to set up and use Redux Toolkit's "RTK Query" data fetching functionality

:::

:::info Prerequisites

- Understanding of [Redux terms and concepts](/tutorials/fundamentals/part-2-concepts-data-flow)

:::

## Introduction

Welcome to the Redux Toolkit Query tutorial! **This tutorial will briefly introduce you to Redux Toolkit's "RTK Query" data fetching capability and teach you how to start using it correctly**.

:::info

For a more in-depth tutorial on RTK Query, see the full ["Redux Essentials" tutorial](/tutorials/essentials/part-7-rtk-query-basics) on the Redux core docs site.

:::

RTK Query is an advanced data fetching and caching tool, designed to simplify common cases for loading data in a web application. RTK Query itself is built on top of the Redux Toolkit core, and leverages RTK's APIs like [`createSlice`](../api/createSlice.mdx) and [`createAsyncThunk`](../api/createAsyncThunk.mdx) to implement its capabilities.

RTK Query is included in the `@reduxjs/toolkit` package as an additional addon. You are not required to use the RTK Query APIs when you use Redux Toolkit, but we think many users will benefit from RTK Query's data fetching and caching in their apps.

### How to Read This Tutorial

For this tutorial, we assume that you're using Redux Toolkit with React, but you can also use it with other UI layers as well. The examples are based on a typical Vite project structure where all the application code is in a `src` folder, but the patterns can be adapted to whatever project or folder setup you're using.

## Setting up your store and API service

To see how RTK Query works, let's walk through a basic usage example. For this example, we'll assume you're using React and want to make use of RTK Query's auto-generated React hooks.

### Create an API service

First, we'll create a service definition that queries the publicly available [PokeAPI](https://pokeapi.co/).

```ts title="src/services/pokemon.ts"
// file: services/types.ts noEmit
export type Pokemon = {}

// file: services/pokemon.ts
// Need to use the React-specific entry point to import createApi
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'
import type { Pokemon } from './types'

// highlight-start
// Define a service using a base URL and expected endpoints
export const pokemonApi = createApi({
  reducerPath: 'pokemonApi',
  baseQuery: fetchBaseQuery({ baseUrl: 'https://pokeapi.co/api/v2/' }),
  endpoints: (builder) => ({
    getPokemonByName: builder.query<Pokemon, string>({
      query: (name) => `pokemon/${name}`,
    }),
  }),
})
//highlight-end

// highlight-start
// Export hooks for usage in functional components, which are
// auto-generated based on the defined endpoints
export const { useGetPokemonByNameQuery } = pokemonApi
// highlight-end
```

With RTK Query, you usually define your entire API definition in one place. This is most likely different from what you see with other libraries such as `swr` or `react-query`, and there are several reasons for that. Our perspective is that it's _much_ easier to keep track of how requests, cache invalidation, and general app configuration behave when they're all in one central location in comparison to having X number of custom hooks in different files throughout your application.

:::tip

Typically, you should only have one API slice per base URL that your application needs to communicate with. For example, if your site fetches data from both `/api/posts` and `/api/users`, you would have a single API slice with `/api/` as the base URL, and separate endpoint definitions for `posts` and `users`. This allows you to effectively take advantage of [automated re-fetching](../rtk-query/usage/automated-refetching.mdx) by defining [tag](../rtk-query/usage/automated-refetching.mdx#tags) relationships across endpoints.

For maintainability purposes, you may wish to split up endpoint definitions across multiple files, while still maintaining a single API slice which includes all of these endpoints. See [code splitting](../rtk-query/usage/code-splitting.mdx) for how you can use the `injectEndpoints` property to inject API endpoints from other files into a single API slice definition.

:::

### Add the service to your store

An RTKQ service generates a "slice reducer" that should be included in the Redux root reducer, and a custom middleware that handles the data fetching. Both need to be added to the Redux store.

```ts title="src/store.ts"
// file: services/pokemon.ts noEmit
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'

export const pokemonApi = createApi({
  reducerPath: 'pokemonApi',
  baseQuery: fetchBaseQuery({ baseUrl: 'https://pokeapi.co/api/v2/' }),
  endpoints: (builder) => ({
    getPokemonByName: builder.query({
      query: (name: string) => `pokemon/${name}`,
    }),
  }),
})

// file: store.ts
import { configureStore } from '@reduxjs/toolkit'
// Or from '@reduxjs/toolkit/query/react'
import { setupListeners } from '@reduxjs/toolkit/query'
import { pokemonApi } from './services/pokemon'

export const store = configureStore({
  reducer: {
    // highlight-start
    // Add the generated reducer as a specific top-level slice
    [pokemonApi.reducerPath]: pokemonApi.reducer,
    // highlight-end
  },
  // highlight-start
  // Adding the api middleware enables caching, invalidation, polling,
  // and other useful features of `rtk-query`.
  middleware: (getDefaultMiddleware) =>
    getDefaultMiddleware().concat(pokemonApi.middleware),
  // highlight-end
})

// optional, but required for refetchOnFocus/refetchOnReconnect behaviors
// see `setupListeners` docs - takes an optional callback as the 2nd arg for customization
setupListeners(store.dispatch)
```

### Wrap your application with the `Provider`

If you haven't already done so, follow the standard pattern for providing the Redux store to the rest of your React application component tree:

```ts title="src/index.tsx"
// file: App.tsx noEmit
import React from 'react'
export default function App() {
  return <div>...</div>
}

// file: store.ts noEmit
import { configureStore } from '@reduxjs/toolkit'

export const store = configureStore({
  reducer: {},
})

// file: index.tsx
import * as React from 'react'
import { createRoot } from 'react-dom/client'
import { Provider } from 'react-redux'

import App from './App'
import { store } from './store'

const container = document.getElementById('root')

if (container) {
  const root = createRoot(container)

  root.render(
    <Provider store={store}>
      <App />
    </Provider>,
  )
} else {
  throw new Error(
    "Root element with ID 'root' was not found in the document. Ensure there is a corresponding HTML element with the ID 'root' in your HTML file.",
  )
}
```

## Use the query in a component

Once a service has been defined, you can import the hooks to make a request.

```ts title="src/App.tsx"
// file: services/pokemon.ts noEmit
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'

export const pokemonApi = createApi({
  reducerPath: 'pokemonApi',
  baseQuery: fetchBaseQuery({ baseUrl: 'https://pokeapi.co/api/v2/' }),
  endpoints: (builder) => ({
    getPokemonByName: builder.query({
      query: (name: string) => `pokemon/${name}`,
    }),
  }),
})

export const { useGetPokemonByNameQuery } = pokemonApi

// file: App.tsx
import * as React from 'react'
// highlight-next-line
import { useGetPokemonByNameQuery } from './services/pokemon'

export default function App() {
  // highlight-start
  // Using a query hook automatically fetches data and returns query values
  const { data, error, isLoading } = useGetPokemonByNameQuery('bulbasaur')
  // Individual hooks are also accessible under the generated endpoints:
  // const { data, error, isLoading } = pokemonApi.endpoints.getPokemonByName.useQuery('bulbasaur')
  // highlight-end

  return (
    <div className="App">
      {error ? (
        <>Oh no, there was an error</>
      ) : isLoading ? (
        <>Loading...</>
      ) : data ? (
        <>
          <h3>{data.species.name}</h3>
          <img src={data.sprites.front_shiny} alt={data.species.name} />
        </>
      ) : null}
    </div>
  )
}
```

When making a request, you're able to track the state in several ways. You can always check `data`, `status`, and `error` to determine the right UI to render. In addition, `useQuery` also provides utility booleans like `isLoading`, `isFetching`, `isSuccess`, and `isError` for the latest request.

#### Basic Example

<iframe
  src="https://codesandbox.io/embed/github/reduxjs/redux-toolkit/tree/master/examples/query/react/basic?fontsize=12&runonclick=1&hidenavigation=1&theme=dark"
  style={{
    width: '100%',
    height: '500px',
    border: 0,
    borderRadius: '4px',
    overflow: 'hidden',
  }}
  title="rtk-query-getting-started-basic"
  allow="geolocation; microphone; camera; midi; vr; accelerometer; gyroscope; payment; ambient-light-sensor; encrypted-media; usb"
  sandbox="allow-modals allow-forms allow-popups allow-scripts allow-same-origin"
></iframe>

Okay, that's interesting... but what if you wanted to show multiple pokemon at the same time? What happens if multiple components load the same pokemon?

#### Advanced example

RTK Query ensures that any component that subscribes to the same query will always use the same data. RTK Query automatically de-dupes requests so you don't have to worry about checking in-flight requests and performance optimizations on your end.

To see this, move the query hook into a `Pokemon` component and render several of them:

```tsx no-transpile title="src/Pokemon.tsx"
import { useGetPokemonByNameQuery } from './services/pokemon'

export const Pokemon = ({
  name,
  pollingInterval,
}: {
  name: string
  pollingInterval: number
}) => {
  const { data, error, isLoading, isFetching } = useGetPokemonByNameQuery(
    name,
    { pollingInterval },
  )

  if (error) return <>Oh no, there was an error</>
  if (isLoading) return <>Loading...</>
  if (!data) return null

  return (
    <>
      <h3>
        {data.species.name} {isFetching ? '...' : ''}
      </h3>
      <img src={data.sprites.front_shiny} alt={data.species.name} />
    </>
  )
}
```

```tsx no-transpile title="src/App.tsx"
import { useState } from 'react'
import { Pokemon } from './Pokemon'

const pokemon = ['bulbasaur', 'pikachu', 'ditto', 'bulbasaur']

export default function App() {
  const [pollingInterval, setPollingInterval] = useState(0)

  return (
    <div className="App">
      <select
        onChange={(change) => setPollingInterval(Number(change.target.value))}
      >
        <option value={0}>Off</option>
        <option value={1000}>1s</option>
        <option value={5000}>5s</option>
      </select>
      {pokemon.map((poke, index) => (
        <Pokemon key={index} name={poke} pollingInterval={pollingInterval} />
      ))}
    </div>
  )
}
```

Run this and check the Network panel in your browser's dev tools. You will see 3 requests, even though there are 4 subscribed components - `bulbasaur` only makes one request, and the loading state is synchronized between the two components. Try changing the dropdown from `Off` to `1s` to see this behavior continue when the queries re-run on a polling interval.

---

### Usage Immer Reducers (`docs/usage/immer-reducers.md`)

---
id: immer-reducers
title: Writing Reducers with Immer
sidebar_label: Writing Reducers with Immer
hide_title: true
---

&nbsp;

# Writing Reducers with Immer

Redux Toolkit's [`createReducer`](../api/createReducer.mdx) and [`createSlice`](../api/createSlice.mdx) automatically use [Immer](https://immerjs.github.io/immer/) internally to let you write simpler immutable update logic using "mutating" syntax. This helps simplify most reducer implementations.

Because Immer is itself an abstraction layer, it's important to understand why Redux Toolkit uses Immer, and how to use it correctly.

## Immutability and Redux

### Basics of Immutability

"Mutable" means "changeable". If something is "immutable", it can never be changed.

JavaScript objects and arrays are all mutable by default. If I create an object, I can change the contents of its fields. If I create an array, I can change the contents as well:

```js
const obj = { a: 1, b: 2 }
// still the same object outside, but the contents have changed
obj.b = 3

const arr = ['a', 'b']
// In the same way, we can change the contents of this array
arr.push('c')
arr[1] = 'd'
```

This is called _mutating_ the object or array. It's the same object or array reference in memory, but now the contents inside the object have changed.

**In order to update values immutably, your code must make _copies_ of existing objects/arrays, and then modify the copies**.

We can do this by hand using JavaScript's array / object spread operators, as well as array methods that return new copies of the array instead of mutating the original array:

```js
const obj = {
  a: {
    // To safely update obj.a.c, we have to copy each piece
    c: 3,
  },
  b: 2,
}

const obj2 = {
  // copy obj
  ...obj,
  // overwrite a
  a: {
    // copy obj.a
    ...obj.a,
    // overwrite c
    c: 42,
  },
}

const arr = ['a', 'b']
// Create a new copy of arr, with "c" appended to the end
const arr2 = arr.concat('c')

// or, we can make a copy of the original array:
const arr3 = arr.slice()
// and mutate the copy:
arr3.push('c')
```

:::info Want to Know More?

For more info on how immutability works in JavaScript, see:

- [A Visual Guide to References in JavaScript](https://daveceddia.com/javascript-references/)
- [Immutability in React and Redux: The Complete Guide](https://daveceddia.com/react-redux-immutability-guide/)

:::

### Reducers and Immutable Updates

One of the primary rules of Redux is that **our reducers are _never_ allowed to mutate the original / current state values!**

:::warning

```js
// ❌ Illegal - by default, this will mutate the state!
state.value = 123
```

:::

There are several reasons why you must not mutate state in Redux:

- It causes bugs, such as the UI not updating properly to show the latest values
- It makes it harder to understand why and how the state has been updated
- It makes it harder to write tests
- It breaks the ability to use "time-travel debugging" correctly
- It goes against the intended spirit and usage patterns for Redux

So if we can't change the originals, how do we return an updated state?

:::tip

**Reducers can only make _copies_ of the original values, and then they can mutate the copies.**

```js
// ✅ This is safe, because we made a copy
return {
  ...state,
  value: 123,
}
```

:::

We already saw that we can write immutable updates by hand, by using JavaScript's array / object spread operators and other functions that return copies of the original values.

This becomes harder when the data is nested. **A critical rule of immutable updates is that you must make a copy of _every_ level of nesting that needs to be updated.**

A typical example of this might look like:

```js
function handwrittenReducer(state, action) {
  return {
    ...state,
    first: {
      ...state.first,
      second: {
        ...state.first.second,
        [action.someId]: {
          ...state.first.second[action.someId],
          fourth: action.someValue,
        },
      },
    },
  }
}
```

However, if you're thinking that "writing immutable updates by hand this way looks hard to remember and do correctly"... yeah, you're right! :)

Writing immutable update logic by hand _is_ hard, and **accidentally mutating state in reducers is the single most common mistake Redux users make**. If you do need to write updates by hand, [Immutable Update Patterns](/usage/structuring-reducers/immutable-update-patterns) covers the correct patterns for nested objects and arrays and the mistakes that most often cause accidental mutations.

## Immutable Updates with Immer

[Immer](https://immerjs.github.io/immer/) is a library that simplifies the process of writing immutable update logic.

Immer provides a function called `produce`, which accepts two arguments: your original `state`, and a callback function. The callback function is given a "draft" version of that state, and inside the callback, it is safe to write code that mutates the draft value. Immer tracks all attempts to mutate the draft value and then replays those mutations using their immutable equivalents to create a safe, immutably updated result:

```js
import { produce } from 'immer'

const baseState = [
  {
    todo: 'Learn typescript',
    done: true,
  },
  {
    todo: 'Try immer',
    done: false,
  },
]

const nextState = produce(baseState, (draftState) => {
  // "mutate" the draft array
  draftState.push({ todo: 'Tweet about it' })
  // "mutate" the nested state
  draftState[1].done = true
})

console.log(baseState === nextState)
// false - the array was copied
console.log(baseState[0] === nextState[0])
// true - the first item was unchanged, so same reference
console.log(baseState[1] === nextState[1])
// false - the second item was copied and updated
```

### Redux Toolkit and Immer

Redux Toolkit's [`createReducer` API](../api/createReducer.mdx) uses Immer internally automatically. So, it's already safe to "mutate" state inside of any case reducer function that is passed to `createReducer`:

```js
const todosReducer = createReducer([], (builder) => {
  builder.addCase('todos/todoAdded', (state, action) => {
    // "mutate" the array by calling push()
    state.push(action.payload)
  })
})
```

In turn, `createSlice` uses `createReducer` inside, so it's also safe to "mutate" state there as well:

```js
const todosSlice = createSlice({
  name: 'todos',
  initialState: [],
  reducers: {
    todoAdded(state, action) {
      state.push(action.payload)
    },
  },
})
```

This even applies if the case reducer functions are defined outside of the `createSlice/createReducer` call. For example, you could have a reusable case reducer function that expects to "mutate" its state, and include it as needed:

```js
const addItemToArray = (state, action) => {
  state.push(action.payload)
}

const todosSlice = createSlice({
  name: 'todos',
  initialState: [],
  reducers: {
    todoAdded: addItemToArray,
  },
})
```

This works because the "mutating" logic is wrapped in Immer's `produce` method internally when it executes.

:::caution

Remember, **the "mutating" logic _only_ works correctly when wrapped inside of Immer!** Otherwise, that code _will_ really mutate the data.

:::

## Immer Usage Patterns

There are several useful patterns to know about and gotchas to watch out for when using Immer in Redux Toolkit.

### Mutating and Returning State

Immer works by tracking attempts to mutate an existing drafted state value, either by assigning to nested fields or by calling functions that mutate the value. That means that **the `state` must be a JS object or array in order for Immer to see the attempted changes**. (You can still have a slice's state be a primitive like a string or a boolean, but since primitives can never be mutated anyway, all you can do is just return a new value.)

In any given case reducer, **Immer expects that you will either _mutate_ the existing state, _or_ construct a new state value yourself and return it, but _not_ both in the same function!** For example, both of these are valid reducers with Immer:

```js
const todosSlice = createSlice({
  name: 'todos',
  initialState: [],
  reducers: {
    todoAdded(state, action) {
      // "Mutate" the existing state, no return value needed
      state.push(action.payload)
    },
    todoDeleted(state, action) {
      // Construct a new result array immutably and return it
      return state.filter((todo) => todo.id !== action.payload)
    },
  },
})
```

However, it _is_ possible to use immutable updates to do part of the work and then save the results via a "mutation". An example of this might be filtering a nested array:

```js
const todosSlice = createSlice({
  name: 'todos',
  initialState: { todos: [], status: 'idle' },
  reducers: {
    todoDeleted(state, action) {
      // Construct a new array immutably
      const newTodos = state.todos.filter((todo) => todo.id !== action.payload)
      // "Mutate" the existing state to save the new array
      state.todos = newTodos
    },
  },
})
```

Note that **mutating state in an arrow function with an implicit return breaks this rule and causes an error!** This is because statements and function calls may return a value, and Immer sees both the attempted mutation and _and_ the new returned value and doesn't know which to use as the result. Some potential solutions are using the `void` keyword to skip having a return value, or using curly braces to give the arrow function a body and no return value:

```js
const todosSlice = createSlice({
  name: 'todos',
  initialState: [],
  reducers: {
    // ❌ ERROR: mutates state, but also returns new array size!
    brokenReducer: (state, action) => state.push(action.payload),
    // ✅ SAFE: the `void` keyword prevents a return value
    fixedReducer1: (state, action) => void state.push(action.payload),
    // ✅ SAFE: curly braces make this a function body and no return
    fixedReducer2: (state, action) => {
      state.push(action.payload)
    },
  },
})
```

While writing nested immutable update logic is hard, there are times when it _is_ simpler to do an object spread operation to update multiple fields at once, vs assigning individual fields:

```js
function objectCaseReducer1(state, action) {
  const { a, b, c, d } = action.payload
  return {
    ...state,
    a,
    b,
    c,
    d,
  }
}

function objectCaseReducer2(state, action) {
  const { a, b, c, d } = action.payload
  // This works, but we keep having to repeat `state.x =`
  state.a = a
  state.b = b
  state.c = c
  state.d = d
}
```

As an alternative, you can use `Object.assign` to mutate multiple fields at once, since `Object.assign` always mutates the first object that it's given:

```js
function objectCaseReducer3(state, action) {
  const { a, b, c, d } = action.payload
  Object.assign(state, { a, b, c, d })
}
```

### Resetting and Replacing State

Sometimes you may want to replace the entire existing `state`, either because you've loaded some new data, or you want to reset the state back to its initial value.

:::warning

**A common mistake is to try assigning `state = someValue` directly. This will not work!** This only points the local `state` variable to a different reference. That is neither mutating the existing `state` object/array in memory, nor returning an entirely new value, so Immer does not make any actual changes.

:::

Instead, to replace the existing state, you should return the new value directly:

```js
const initialState = []
const todosSlice = createSlice({
  name: 'todos',
  initialState,
  reducers: {
    brokenTodosLoadedReducer(state, action) {
      // ❌ ERROR: does not actually mutate or return anything new!
      state = action.payload
    },
    fixedTodosLoadedReducer(state, action) {
      // ✅ CORRECT: returns a new value to replace the old one
      return action.payload
    },
    correctResetTodosReducer(state, action) {
      // ✅ CORRECT: returns a new value to replace the old one
      return initialState
    },
  },
})
```

### Debugging and Inspecting Drafted State

It's common to want to log in-progress state from a reducer to see what it looks like as it's being updated, like `console.log(state)`. Unfortunately, browsers display logged Proxy instances in a format that is hard to read or understand:



To work around this, [Immer includes a `current` function that extracts a copy of the wrapped data](https://immerjs.github.io/immer/current), and RTK re-exports `current`. You can use this in your reducers if you need to log or inspect the work-in-progress state:

```js
import { current } from '@reduxjs/toolkit'

const todosSlice = createSlice({
  name: 'todos',
  initialState: todosAdapter.getInitialState(),
  reducers: {
    todoToggled(state, action) {
      // ❌ ERROR: logs the Proxy-wrapped data
      console.log(state)
      // ✅ CORRECT: logs a plain JS copy of the current data
      console.log(current(state))
    },
  },
})
```

The correct output would look like this instead:



Immer also provides [`original` and `isDraft` functions](https://immerjs.github.io/immer/original), which retrieves the original data without any updates applied and check to see if a given value is a Proxy-wrapped draft. As of RTK 1.5.1, both of those are re-exported from RTK as well.

### Updating Nested Data

Immer greatly simplifies updating nested data. Nested objects and arrays are also wrapped in Proxies and drafted, and it's safe to pull out a nested value into its own variable and then mutate it.

However, this still only applies to objects and arrays. If we pull out a primitive value into its own variable and try to update it, Immer has nothing to wrap and cannot track any updates:

```js
const todosSlice = createSlice({
  name: 'todos',
  initialState: [],
  reducers: {
    brokenTodoToggled(state, action) {
      const todo = state.find((todo) => todo.id === action.payload)
      if (todo) {
        // ❌ ERROR: Immer can't track updates to a primitive value!
        let { completed } = todo
        completed = !completed
      }
    },
    fixedTodoToggled(state, action) {
      const todo = state.find((todo) => todo.id === action.payload)
      if (todo) {
        // ✅ CORRECT: This object is still wrapped in a Proxy, so we can "mutate" it
        todo.completed = !todo.completed
      }
    },
  },
})
```

There _is_ a gotcha here. [Immer will not wrap objects that are newly inserted into the state](https://immerjs.github.io/immer/pitfalls#data-not-originating-from-the-state-will-never-be-drafted). Most of the time this shouldn't matter, but there may be occasions when you want to insert a value and then make further updates to it.

Related to this, RTK's [`createEntityAdapter` update functions](../api/createEntityAdapter.mdx#crud-functions) can either be used as standalone reducers, or "mutating" update functions. These functions determine whether to "mutate" or return a new value by checking to see if the state they're given is wrapped in a draft or not. If you are calling these functions yourself inside of a case reducer, be sure you know whether you're passing them a draft value or a plain value.

Finally, it's worth noting that **Immer does not automatically create nested objects or arrays for you - you have to create them yourself**. As an example, say we have a lookup table containing nested arrays, and we want to insert an item into one of those arrays. If we unconditionally try to insert without checking for the existence of that array, the logic will crash when the array doesn't exist. Instead, you'd need to ensure the array exists first:

```js
const itemsSlice = createSlice({
  name: 'items',
  initialState: { a: [], b: [] },
  reducers: {
    brokenNestedItemAdded(state, action) {
      const { id, item } = action.payload
      // ❌ ERROR: will crash if no array exists for `id`!
      state[id].push(item)
    },
    fixedNestedItemAdded(state, action) {
      const { id, item } = action.payload
      // ✅ CORRECT: ensures the nested array always exists first
      if (!state[id]) {
        state[id] = []
      }

      state[id].push(item)
    },
  },
})
```

### Linting State Mutations

Many ESLint configs include the https://eslint.org/docs/rules/no-param-reassign rule, which may also warn about mutations to nested fields. That can cause the rule to warn about mutations to `state` in Immer-powered reducers, which is not helpful.

To resolve this, you can tell the ESLint rule to ignore mutations and assignment to a parameter named `state` only in slice files:

```js
// @filename .eslintrc.js
module.exports = {
  // add to your ESLint config definition
  overrides: [
    {
      // feel free to replace with your preferred file pattern - eg. 'src/**/*Slice.ts'
      files: ['src/**/*.slice.ts'],
      // avoid state param assignment
      rules: { 'no-param-reassign': ['error', { props: false }] },
    },
  ],
}
```

## Why Immer is Built In

We've received a number of requests over time to make Immer an optional part of RTK's `createSlice` and `createReducer` APIs, rather than strictly required.

Our answer is always the same: **Immer _is required_ in RTK, and that is not going to change**.

It's worth going over the reasons why we consider Immer to be a critical part of RTK and why we will not make it optional.

### Benefits of Immer

Immer has two primary benefits. First, **Immer drastically simplifies immutable update logic**. [Proper immutable updates are extremely verbose](/usage/structuring-reducers/immutable-update-patterns#updating-nested-objects). Those verbose operations are hard to read overall, and also obfuscate what the actual intent of the update statement is. Immer eliminates all the nested spreads and array slices. Not only is the code shorter and easier to read, it's much more clear what actual update is supposed to happen.

Second, [writing immutable updates correctly is _hard_](/usage/structuring-reducers/immutable-update-patterns), and it is really easy to make mistakes (like forgetting to copy a level of nesting in a set of object spreads, copying a top-level array and not the item to be updated inside the array, or forgetting that `array.sort()` mutates the array). This is part of why [accidental mutations has always been the most common cause of Redux bugs](/faq/react-redux#why-isnt-my-component-re-rendering). **Immer effectively _eliminates_ accidental mutations**. Not only are there no more spread operations that can be mis-written, but Immer freezes state automatically as well. This causes errors to be thrown if you do accidentally mutate, even outside of a reducer. **Eliminating the #1 cause of Redux bugs is a _huge_ improvement.**

Additionally, RTK Query uses Immer's patch capabilities to enable [optimistic updates and manual cache updates](../rtk-query/usage/manual-cache-updates.mdx) as well.

### Tradeoffs and Concerns

Like any tool, using Immer does have tradeoffs, and users have expressed a number of concerns about using it.

Immer does add to the overall app bundle size. It's about 8K min, 3.3K min+gz (ref: [Immer docs: Installation](https://immerjs.github.io/immer/installation), [Bundle.js.org analysis](https://bundle.js.org/?q=immer&treeshake=[{default+as+produce+}])). However, that library bundle size starts to pay for itself by shrinking the amount of reducer logic in your app. Additionally, the benefits of more readable code and eliminating mutation bugs are worth the size.

Immer also adds a bit of overhead in runtime performance. However, [per the Immer "Performance" docs page, the overhead is not meaningful in practice](https://immerjs.github.io/immer/performance/). Additionally, [reducers are almost never a perf bottleneck in a Redux app anyway](https://github.com/reduxjs/redux-toolkit/issues/242#issuecomment-583296008). Instead, the cost of updating the UI is much more important.

So, while using Immer isn't "free", the bundle and perf costs are small enough to be worth it.

The most realistic pain point with using Immer is that browser debuggers show Proxies in a confusing way, which makes it hard to inspect state variables while debugging. This is certainly an annoyance. However, this doesn't actually affect runtime behavior, and we've [documented the use of `current` to create a viewable plain JS version of the data](#debugging-and-inspecting-drafted-state) above in this page. (Given the increasingly wide use of Proxies as part of libraries like Mobx and Vue 3, this is also not unique to Immer.)

Another issue is education and understanding. Redux has always required immutability in reducers, and so seeing "mutating" code can be confusing. It's certainly possible that new Redux users might see those "mutations" in example code, assume that it's normal for Redux usage, and later try to do the same thing outside of `createSlice`. This would indeed cause real mutations and bugs, because it's outside of Immer's ability to wrap the updates.

We've addressed this by [repeatedly emphasizing the important of immutability throughout our docs](/tutorials/essentials/part-1-overview-concepts#immutability), including multiple highlighted sections emphasizing that [the "mutations" only work right thanks to Immer's "magic" inside](/tutorials/essentials/part-2-app-structure#reducers-and-immutable-updates) and adding this specific docs page you're reading now.

### Architecture and Intent

There's two more reasons why Immer is not optional.

One is RTK's architecture. `createSlice` and `createReducer` are implemented by directly importing Immer. There's no easy way to create a version of either of them that would have a hypothetical `immer: false` option. You can't do optional imports, and we need Immer available immediately and synchronously during the initial load of the app.

And finally: **Immer is built into RTK by default because we believe it is the best choice for our users!** We _want_ our users to be using Immer, and consider it to be a critical non-negotiable component of RTK. The great benefits like simpler reducer code and preventing accidental mutations far outweigh the relatively small concerns.

## Further Information

See [the Immer documentation](https://immerjs.github.io/immer/) for more details on Immer's APIs, edge cases, and behavior.

For historical discussion on why Immer is required, see these issues:

- [RTK #5: Why Immer inside a starter kit?](https://github.com/reduxjs/redux-toolkit/issues/5)
- [RTK #183: Consider adding an option to remove Immer](https://github.com/reduxjs/redux-toolkit/issues/183)
- [RTK #242: make `immer` optional for `createReducer`](https://github.com/reduxjs/redux-toolkit/issues/242)

---

### Usage Usage Guide (`docs/usage/usage-guide.md`)

---
id: usage-guide
title: Usage Guide
sidebar_label: Usage Guide
hide_title: true
---

&nbsp;

# Usage Guide

Redux Toolkit is the standard way to write Redux logic. It includes the store setup, reducer creation, async logic, data fetching, and normalized state utilities that a typical Redux app needs, with good defaults and development checks built in.

This page is an overview of which Redux Toolkit API to reach for in each part of your app, with a short example of each and links to the full API reference and usage pages. It assumes you already know the Redux concepts covered in the [Redux Essentials tutorial](/tutorials/essentials/part-1-overview-concepts). If you're coming from a hand-written Redux codebase, [Migrating to Modern Redux](/usage/migrating-to-modern-redux) walks through converting each pattern to its Redux Toolkit equivalent.

## Store Setup

[`configureStore`](../api/configureStore.mdx) creates the Redux store. It accepts an options object instead of positional arguments, automatically calls `combineReducers` when given an object of slice reducers, adds the [thunk middleware](../api/getDefaultMiddleware.mdx) and the development-only [immutability](../api/immutabilityMiddleware.mdx) and [serializability](../api/serializabilityMiddleware.mdx) check middleware, and enables the Redux DevTools Extension.

The simplest setup is an object of slice reducers:

```ts
import { configureStore } from '@reduxjs/toolkit'
import usersReducer from '../features/users/usersSlice'
import postsReducer from '../features/posts/postsSlice'

export const store = configureStore({
  reducer: {
    users: usersReducer,
    posts: postsReducer,
  },
})
```

If you need to add middleware or enhancers, use the callback forms so the defaults stay in place:

```ts
import { configureStore } from '@reduxjs/toolkit'
import rootReducer from './rootReducer'
import { loggerMiddleware } from './middleware/logger'
import { monitorReducersEnhancer } from './enhancers/monitorReducers'

export const store = configureStore({
  reducer: rootReducer,
  middleware: (getDefaultMiddleware) =>
    getDefaultMiddleware().concat(loggerMiddleware),
  enhancers: (getDefaultEnhancers) =>
    getDefaultEnhancers().concat(monitorReducersEnhancer),
})
```

The `reducer` object form only handles one level of nesting. For nested reducers, call `combineReducers` yourself, or use [`combineSlices`](../api/combineSlices.mdx), which also supports lazily injecting slice reducers for code-split apps.

For the full list of options, including `preloadedState` and DevTools configuration, see the [`configureStore` API reference](../api/configureStore.mdx). The Redux core docs have a longer walkthrough in [Configuring Your Store](/usage/configuring-your-store).

## Writing Reducers and Actions with `createSlice`

[`createSlice`](../api/createSlice.mdx) is the standard way to define a piece of Redux state, the reducer that updates it, and the action creators that trigger those updates. You give it a `name`, an `initialState`, and an object of "case reducer" functions. It generates an action type string and an action creator for each case reducer, and combines the case reducers into a single slice reducer:

```ts
import { createSlice } from '@reduxjs/toolkit'
import type { PayloadAction } from '@reduxjs/toolkit'

interface Post {
  id: string
  title: string
}

const initialState: Post[] = []

const postsSlice = createSlice({
  name: 'posts',
  initialState,
  reducers: {
    postAdded(state, action: PayloadAction<Post>) {
      state.push(action.payload)
    },
    postRemoved(state, action: PayloadAction<string>) {
      return state.filter((post) => post.id !== action.payload)
    },
  },
})

// `postAdded` creates actions with type "posts/postAdded"
export const { postAdded, postRemoved } = postsSlice.actions
export default postsSlice.reducer
```

Case reducers run inside [Immer](https://immerjs.github.io/immer/), so they can "mutate" the `state` argument and Immer produces a correct immutable update. They can also return a new value instead. See [Writing Reducers with Immer](./immer-reducers.md) for how this works and the rules to follow.

Export the action creators and the reducer by name, as shown above, and import them where they're needed. Two things to keep in mind when organizing slices:

- **Action types are not exclusive to one slice.** Any slice can respond to any action via the `extraReducers` option. For example, several slices might reset their state when a `userLoggedOut` action is dispatched.
- **Slice files can import each other's actions.** `createSlice` builds its reducer lazily on first use, so a circular import between two slice files that reference each other in `extraReducers` works. If you'd rather avoid the circular import entirely, define the shared action in its own file with [`createAction`](../api/createAction.mdx) and import it into both slices.

[`createReducer`](../api/createReducer.mdx) and [`createAction`](../api/createAction.mdx) are the underlying pieces that `createSlice` uses. You can use them directly for a reducer that isn't tied to a slice, or for an action shared across slices. Action creators made by either API carry their type string as `actionCreator.type` and have a `.match()` method for type-safe checks. If you use one in a `switch` statement, compare against `actionCreator.type`, not the action creator itself:

```ts
import type { UnknownAction } from '@reduxjs/toolkit'

const reducer = (state = initialState, action: UnknownAction) => {
  switch (action.type) {
    // ❌ This is a function, not a string, and will never match
    case postAdded:
    // ✅ Compare against the generated type string
    case postAdded.type: {
      // ...
    }
  }
}
```

## Async Logic and Data Fetching

A Redux store is synchronous. Any async work, such as fetching data from a server, has to happen outside the reducers and dispatch plain actions when it has results. Redux Toolkit provides several APIs for this, depending on what the async work is.

### Fetching Server Data with RTK Query

For loading and caching data from a server, use [**RTK Query**](../rtk-query/overview.md). It is built into Redux Toolkit and handles the request lifecycle, caching, loading state, invalidation, and refetching for you. You define an "API slice" with endpoints, and it generates React hooks and thunks for each one, so you don't have to write thunks, reducers, or loading flags for data fetching:

```ts
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'

interface User {
  id: number
  name: string
}

export const usersApi = createApi({
  reducerPath: 'usersApi',
  baseQuery: fetchBaseQuery({ baseUrl: '/api' }),
  endpoints: (build) => ({
    getUsers: build.query<User[], void>({
      query: () => 'users',
    }),
  }),
})

export const { useGetUsersQuery } = usersApi
```

See the [RTK Query Quick Start](../tutorials/rtk-query.mdx) for a complete setup and [RTK Query Overview](../rtk-query/overview.md) for how it fits into a Redux app.

### Other Async Logic with Thunks and `createAsyncThunk`

For async logic that isn't a straightforward server request, such as chaining several dispatches, checking state before deciding what to do, or calling browser APIs, write a [thunk](/usage/writing-logic-thunks). `configureStore` includes the thunk middleware by default. Thunks are usually written in the same file as the slice whose actions they dispatch.

If the thunk's job is "run a promise and record its pending, fulfilled, and rejected result in the store", use [`createAsyncThunk`](../api/createAsyncThunk.mdx). It generates the three lifecycle action types and a thunk that dispatches them around your promise:

```ts
import { createAsyncThunk, createSlice } from '@reduxjs/toolkit'
import { userAPI } from './userAPI'
import type { User } from './types'

export const fetchUserById = createAsyncThunk(
  'users/fetchById',
  async (userId: number) => {
    const response = await userAPI.fetchById(userId)
    return response.data
  },
)

const usersSlice = createSlice({
  name: 'users',
  initialState: {
    entities: [] as User[],
    loading: 'idle' as 'idle' | 'pending',
  },
  reducers: {},
  extraReducers: (builder) => {
    builder
      .addCase(fetchUserById.pending, (state) => {
        state.loading = 'pending'
      })
      .addCase(fetchUserById.fulfilled, (state, action) => {
        state.loading = 'idle'
        state.entities.push(action.payload)
      })
  },
})

// Later, in a component or another thunk:
dispatch(fetchUserById(123))
```

The payload creator receives the thunk argument and a `thunkAPI` object with `dispatch`, `getState`, `extra`, `requestId`, `signal`, and `rejectWithValue`. The [`createAsyncThunk` API reference](../api/createAsyncThunk.mdx) covers error handling, cancellation, and conditional execution.

### Reacting to Actions with the Listener Middleware

[`createListenerMiddleware`](../api/createListenerMiddleware.mdx) runs logic in response to dispatched actions or state changes, outside of components and reducers. It's a lightweight alternative to sagas and observables for workflows like "when this action is dispatched, wait for that one, then do something", debouncing, or side effects that don't belong in a component. See the [listener middleware API reference](../api/createListenerMiddleware.mdx) for usage patterns and [Side Effects Approaches](/usage/side-effects-approaches) for how it compares to the other options.

## Managing Normalized Data

Relational or nested data is usually easiest to work with in a Redux store when it's [normalized](/usage/structuring-reducers/normalizing-state-shape): each type of item is stored in a lookup table keyed by ID, along with an array of IDs for ordering. Redux Toolkit's [`createEntityAdapter`](../api/createEntityAdapter.mdx) implements that `{ ids: [], entities: {} }` shape and generates reducer functions and selectors that work with it.

```
/* Detailed source-code truncated for AI context efficiency. */
```

If your items don't use an `id` field, pass a `selectId` option: `createEntityAdapter<User>({ selectId: (user) => user.idx })`.

The adapter's `setAll`, `addMany`, and `upsertMany` functions accept either an array of items or an object already keyed by ID, so if you normalize an API response before it reaches the reducer (for example, with `normalizr` or by hand in a thunk), you can pass the result straight through. Several slices can each pick their own entity type out of the same fulfilled action's payload.

Note that the adapter's `updateOne`, `updateMany`, `upsertOne`, and `upsertMany` functions do shallow merges: a nested object in the incoming change replaces the whole existing nested object. They work best with flat, normalized items. See the [`createEntityAdapter` API reference](../api/createEntityAdapter.mdx) for the full list of CRUD functions and options, and [Redux Essentials, Part 6](/tutorials/essentials/part-6-performance-normalization) for a worked example.

## Working with Non-Serializable Data

One of the core usage principles for Redux is that [you should not put non-serializable values in state or actions](/style-guide/#do-not-put-non-serializable-values-in-state-or-actions).

However, like most rules, there are exceptions. There may be occasions when you have to deal with actions that need to accept non-serializable data. This should be done very rarely and only if necessary, and these non-serializable payloads shouldn't ever make it into your application state through a reducer.

The [serializability dev check middleware](../api/serializabilityMiddleware.mdx) will automatically warn anytime it detects non-serializable values in your actions or state. We encourage you to leave this middleware active to help avoid accidentally making mistakes. However, if you _do_ need to turn off those warnings, you can customize the middleware by configuring it to ignore specific action types, or fields in actions and state:

```js
configureStore({
  //...
  middleware: (getDefaultMiddleware) =>
    getDefaultMiddleware({
      serializableCheck: {
        // Ignore these action types
        ignoredActions: ['your/action/type'],
        // Ignore these field paths in all actions
        ignoredActionPaths: ['meta.arg', 'payload.timestamp'],
        // Ignore these paths in the state
        ignoredPaths: ['items.dates'],
      },
    }),
})
```

### Persistence Libraries

Persistence libraries are a common source of non-serializable values, because they dispatch their own actions and some of them store values like timestamps or `Promise`s in the store. The examples below show how to configure the serializability check for the most common cases. They are configuration examples, not endorsements of any particular library.

#### Use with Redux-Persist

:::info

[Redux-Persist](https://github.com/rt2zz/redux-persist) has been the most widely used persistence library for Redux, and it still works. However, its last release was v6.0.0 in 2019 and the repository has had no commits since 2021, so it is effectively unmaintained. If you're choosing a persistence library for a new project, also look at [`redux-remember`](https://github.com/zewish/redux-remember), which is actively maintained, persists selected slices to a key-value storage driver of your choice, is tested against Redux 5 and Redux Toolkit 2, and integrates as a store enhancer via `configureStore`'s `enhancers` option without needing any serializability check configuration.

:::

If using Redux-Persist, you should specifically ignore all the action types it dispatches:

```jsx
import { createRoot } from 'react-dom/client'
import { configureStore } from '@reduxjs/toolkit'
import {
  persistStore,
  persistReducer,
  FLUSH,
  REHYDRATE,
  PAUSE,
  PERSIST,
  PURGE,
  REGISTER,
} from 'redux-persist'
import storage from 'redux-persist/lib/storage'
import { PersistGate } from 'redux-persist/integration/react'

import App from './App'
import rootReducer from './reducers'

const persistConfig = {
  key: 'root',
  version: 1,
  storage,
}

const persistedReducer = persistReducer(persistConfig, rootReducer)

const store = configureStore({
  reducer: persistedReducer,
  middleware: (getDefaultMiddleware) =>
    getDefaultMiddleware({
      serializableCheck: {
        ignoredActions: [FLUSH, REHYDRATE, PAUSE, PERSIST, PURGE, REGISTER],
      },
    }),
})

let persistor = persistStore(store)

const container = document.getElementById('root')

if (container) {
  const root = createRoot(container)

  root.render(
    <Provider store={store}>
      <PersistGate loading={null} persistor={persistor}>
        <App />
      </PersistGate>
    </Provider>,
  )
} else {
  throw new Error(
    "Root element with ID 'root' was not found in the document. Ensure there is a corresponding HTML element with the ID 'root' in your HTML file.",
  )
}
```

Additionally, you can purge any persisted state by adding an extra reducer to the specific slice that you would like to clear when calling persistor.purge(). This is especially helpful when you are looking to clear persisted state on a dispatched logout action.

```ts
import { PURGE } from "redux-persist";

...
extraReducers: (builder) => {
    builder.addCase(PURGE, (state) => {
        customEntityAdapter.removeAll(state);
    });
}
```

It is also strongly recommended to blacklist any api(s) that you have configured with RTK Query. If the api slice reducer is not blacklisted, the api cache will be automatically persisted and restored which could leave you with phantom subscriptions from components that do not exist any more. Configuring this should look something like this:

```ts
const persistConfig = {
  key: 'root',
  version: 1,
  storage,
  blacklist: [pokemonApi.reducerPath],
}
```

See [Redux Toolkit #121: How to use this with Redux-Persist?](https://github.com/reduxjs/redux-toolkit/issues/121) and [Redux-Persist #988: non-serializable value error](https://github.com/rt2zz/redux-persist/issues/988#issuecomment-552242978) for further discussion.

#### Use with React-Redux-Firebase

RRF includes timestamp values in most actions and state as of 3.x, but there are PRs that may improve that behavior as of 4.x.

A possible configuration to work with that behavior could look like:

```ts
import { configureStore } from '@reduxjs/toolkit'
import {
  getFirebase,
  actionTypes as rrfActionTypes,
} from 'react-redux-firebase'
import { constants as rfConstants } from 'redux-firestore'
import rootReducer from './rootReducer'

const store = configureStore({
  reducer: rootReducer,
  middleware: (getDefaultMiddleware) =>
    getDefaultMiddleware({
      serializableCheck: {
        ignoredActions: [
          // just ignore every redux-firebase and react-redux-firebase action type
          ...Object.keys(rfConstants.actionTypes).map(
            (type) => `${rfConstants.actionsPrefix}/${type}`,
          ),
          ...Object.keys(rrfActionTypes).map(
            (type) => `@@reactReduxFirebase/${type}`,
          ),
        ],
        ignoredPaths: ['firebase', 'firestore'],
      },
      thunk: {
        extraArgument: {
          getFirebase,
        },
      },
    }),
})

export default store
```

---

### Usage Usage With Typescript (`docs/usage/usage-with-typescript.md`)

---
id: usage-with-typescript
title: Usage With TypeScript
sidebar_label: Usage With TypeScript
hide_title: true
---

&nbsp;

# Usage With TypeScript

:::tip What You'll Learn

- Details on how to use each Redux Toolkit API with TypeScript

:::

## Introduction

Redux Toolkit is written in TypeScript, and its API is designed to enable great integration with TypeScript applications.

This page provides specific details for each of the different APIs included in Redux Toolkit and how to type them correctly with TypeScript.

**See the [Quick Start tutorial page](/tutorials/quick-start) for a brief overview of how to set up and use Redux Toolkit and React Redux to work with TypeScript**.

:::info

If you encounter any problems with the types that are not described on this page, please [open an issue](https://github.com/reduxjs/redux-toolkit/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-desc) for discussion.

:::

:::tip TypeScript Version Requirement

Redux Toolkit follows [DefinitelyTyped's policy](https://github.com/DefinitelyTyped/DefinitelyTyped#support-window) of supporting TypeScript versions released within the past two years. As of RTK 2.11, this means we support:

| RTK Version | Minimum TypeScript |
| ----------- | ------------------ |
| 2.x         | 5.4+               |
| 1.9.x       | 4.7+               |

<details>
<summary>Using an older TypeScript version?</summary>

If you're unable to upgrade TypeScript, RTK may still work with older versions, but you may encounter type errors or missing type inference. We strongly recommend upgrading to take advantage of improved type safety and developer experience.

</details>

:::

## `configureStore`

The standard setup is shown in [Usage with TypeScript: Define Root State and Dispatch Types](/usage/usage-with-typescript#define-root-state-and-dispatch-types) in the Redux docs: create the store, then extract `RootState` and `AppDispatch` from it, and export pre-typed `useAppSelector` and `useAppDispatch` hooks. Here are some additional details.

### Getting the `State` type

`configureStore` infers the state type from its `reducer` argument, whether you pass a single root reducer or an object of slice reducers. Either way, the safest source for `RootState` is the store itself:

```ts
import { configureStore } from '@reduxjs/toolkit'
// ...
const store = configureStore({
  reducer: {
    one: oneSlice.reducer,
    two: twoSlice.reducer,
  },
})
export type RootState = ReturnType<typeof store.getState>
```

If you define a `rootReducer` with `combineReducers` first, you can also extract the type from the reducer directly with `ReturnType<typeof rootReducer>`. This is useful when the state type is needed in a file that cannot import the store without a circular import.

### Getting the `Dispatch` type

Extract `AppDispatch` from the store after creating it. It includes the type changes contributed by any middleware you added, such as the thunk middleware's overloads:

```ts
export type AppDispatch = typeof store.dispatch
```

### Correct typings for the `Dispatch` type

The type of the `dispatch` function type will be directly inferred from the `middleware` option. So if you add _correctly typed_ middlewares, `dispatch` should already be correctly typed.

As TypeScript often widens array types when combining arrays using the spread operator, we suggest using the `.concat(...)` and `.prepend(...)` methods of the `Tuple` returned by `getDefaultMiddleware()`.

```ts
import { configureStore } from '@reduxjs/toolkit'
import additionalMiddleware from 'additional-middleware'
import logger from 'redux-logger'
// @ts-ignore
import untypedMiddleware from 'untyped-middleware'
import rootReducer from './rootReducer'

export type RootState = ReturnType<typeof rootReducer>
const store = configureStore({
  reducer: rootReducer,
  // highlight-start
  middleware: (getDefaultMiddleware) =>
    getDefaultMiddleware()
      .prepend(
        // correctly typed middlewares can just be used
        additionalMiddleware,
        // you can also type middlewares manually
        untypedMiddleware as Middleware<
          (action: Action<'specialAction'>) => number,
          RootState
        >,
      )
      // prepend and concat calls can be chained
      .concat(logger),
  // highlight-end
})

export type AppDispatch = typeof store.dispatch

export default store
```

#### Using `Tuple` without `getDefaultMiddleware`

If you want to skip the usage of `getDefaultMiddleware` altogether, you are required to use `Tuple` for type-safe creation of your `middleware` array. This class extends the default JavaScript `Array` type, only with modified typings for `.concat(...)` and the additional `.prepend(...)` method.

For example:

```ts
import { configureStore, Tuple } from '@reduxjs/toolkit'

configureStore({
  reducer: rootReducer,
  middleware: () => new Tuple(additionalMiddleware, logger),
})
```

### Using the extracted `Dispatch` type with React Redux

By default, the React Redux `useDispatch` hook does not contain any types that take middlewares into account. Use the pre-typed `useAppDispatch` hook from [Define Typed Hooks](/usage/usage-with-typescript#define-typed-hooks) so that dispatching thunks and other middleware-handled values is typed correctly.

## `createAction`

For most use cases, there is no need to have a literal definition of `action.type`, so the following can be used:

```typescript
createAction<number>('test')
```

This will result in the created action being of type `PayloadActionCreator<number, string>`.

In some setups, you will need a literal type for `action.type`, though.
Unfortunately, TypeScript type definitions do not allow for a mix of manually-defined and inferred type parameters, so you'll have to specify the `type` both in the Generic definition as well as in the actual JavaScript code:

```typescript
createAction<number, 'test'>('test')
```

If you are looking for an alternate way of writing this without the duplication, you can use a prepare callback so that both type parameters can be inferred from arguments, removing the need to specify the action type.

```typescript
function withPayloadType<T>() {
  return (t: T) => ({ payload: t })
}
createAction('test', withPayloadType<string>())
```

### Alternative to using a literally-typed `action.type`

If you are using `action.type` as a discriminator on a discriminated union, for example to correctly type your payload in `case` statements, you might be interested in this alternative:

Created action creators have a `match` method that acts as a [type predicate](https://www.typescriptlang.org/docs/handbook/2/narrowing.html#using-type-predicates):

```typescript
const increment = createAction<number>('increment')
function test(action: Action) {
  if (increment.match(action)) {
    // action.payload inferred correctly here
    action.payload
  }
}
```

This `match` method is also very useful in combination with `redux-observable` and RxJS's `filter` method.

## `createReducer`

### Building Type-Safe Reducer Argument Objects

The second parameter for `createReducer` is a callback that receives a `ActionReducerMapBuilder` instance:

```typescript {3-10}
const increment = createAction<number, 'increment'>('increment')
const decrement = createAction<number, 'decrement'>('decrement')
createReducer(0, (builder) =>
  builder
    .addCase(increment, (state, action) => {
      // action is inferred correctly here
    })
    .addCase(decrement, (state, action: PayloadAction<string>) => {
      // this would error out
    }),
)
```

#### Typing `builder.addMatcher`

As the first `matcher` argument to `builder.addMatcher`, a [type predicate](https://www.typescriptlang.org/docs/handbook/advanced-types.html#using-type-predicates) function should be used.
As a result, the `action` argument for the second `reducer` argument can be inferred by TypeScript:

```ts
function isNumberValueAction(action: UnknownAction): action is PayloadAction<{ value: number }> {
  return typeof action.payload.value === 'number'
}

createReducer({ value: 0 }, builder =>
   builder.addMatcher(isNumberValueAction, (state, action) => {
      state.value += action.payload.value
   })
})
```

## `createSlice`

As `createSlice` creates your actions as well as your reducer for you, you don't have to worry about type safety here.
Action types can just be provided inline:

```typescript
const slice = createSlice({
  name: 'test',
  initialState: 0,
  reducers: {
    increment: (state, action: PayloadAction<number>) => state + action.payload,
  },
})
// now available:
slice.actions.increment(2)
// also available:
slice.caseReducers.increment(0, { type: 'increment', payload: 5 })
```

If you have too many case reducers and defining them inline would be messy, or you want to reuse case reducers across slices, you can also define them outside the `createSlice` call and type them as `CaseReducer`:

```typescript
type State = number
const increment: CaseReducer<State, PayloadAction<number>> = (state, action) =>
  state + action.payload

createSlice({
  name: 'test',
  initialState: 0,
  reducers: {
    increment,
  },
})
```

### Defining the Initial State Type

You might have noticed that it is not a good idea to pass your `SliceState` type as a generic to `createSlice`. This is due to the fact that in almost all cases, follow-up generic parameters to `createSlice` need to be inferred, and TypeScript cannot mix explicit declaration and inference of generic types within the same "generic block".

The standard approach is to declare an interface or type for your state, create an initial state value that uses that type, and pass the initial state value to `createSlice`. You can also use the construct `initialState: myInitialState satisfies SliceState as SliceState`.

```ts {1,4,8,15}
type SliceState = { state: 'loading' } | { state: 'finished'; data: string }

// First approach: define the initial state using that type
const initialState: SliceState = { state: 'loading' }

createSlice({
  name: 'test1',
  initialState, // type SliceState is inferred for the state of the slice
  reducers: {},
})

// Or, cast the initial state as necessary
createSlice({
  name: 'test2',
  initialState: { state: 'loading' } satisfies SliceState as SliceState,
  reducers: {},
})
```

which will result in a `Slice<SliceState, ...>`.

### Defining Action Contents with `prepare` Callbacks

If you want to add a `meta` or `error` property to your action, or customize the `payload` of your action, you have to use the `prepare` notation.

Using this notation with TypeScript looks like this:

```ts {5-16}
const blogSlice = createSlice({
  name: 'blogData',
  initialState,
  reducers: {
    receivedAll: {
      reducer(
        state,
        action: PayloadAction<Page[], string, { currentPage: number }>,
      ) {
        state.all = action.payload
        state.meta = action.meta
      },
      prepare(payload: Page[], currentPage: number) {
        return { payload, meta: { currentPage } }
      },
    },
  },
})
```

### Generated Action Types for Slices

`createSlice` generates action type strings by combining the `name` field from the slice with the field name of the reducer function, like `'test/increment'`. This is strongly typed as the exact value, thanks to TS's string literal analysis.

You can also use the `slice.action.myAction.match` [type predicate](https://www.typescriptlang.org/docs/handbook/advanced-types.html#using-type-predicates), which will narrow down an action object to the exact type:

```ts {10}
const slice = createSlice({
  name: 'test',
  initialState: 0,
  reducers: {
    increment: (state, action: PayloadAction<number>) => state + action.payload,
  },
})

type incrementType = typeof slice.actions.increment.type
// type incrementType = 'test/increment'

function myCustomMiddleware(action: Action) {
  if (slice.actions.increment.match(action)) {
    // `action` is narrowed down to the type `PayloadAction<number>` here.
  }
}
```

If you actually _need_ that type, unfortunately there is no other way than manual casting.

### Type safety with `extraReducers`

Reducer lookup tables that map an action `type` string to a reducer function are not easy to fully type correctly. This affects both `createReducer` and the `extraReducers` argument for `createSlice`. So, like with `createReducer`, [you should use the "builder callback" approach](#building-type-safe-reducer-argument-objects) for defining the reducer object argument.

This is particularly useful when a slice reducer needs to handle action types generated by other slices, or generated by specific calls to `createAction` (such as the actions generated by [`createAsyncThunk`](../api/createAsyncThunk.mdx)).

```ts {27-30}
const fetchUserById = createAsyncThunk(
  'users/fetchById',
  // if you type your function argument here
  async (userId: number) => {
    const response = await fetch(`https://reqres.in/api/users/${userId}`)
    return (await response.json()) as Returned
  },
)

interface UsersState {
  entities: User[]
  loading: 'idle' | 'pending' | 'succeeded' | 'failed'
}

const initialState = {
  entities: [],
  loading: 'idle',
} satisfies UsersState as UsersState

const usersSlice = createSlice({
  name: 'users',
  initialState,
  reducers: {
    // fill in primary logic here
  },
  extraReducers: (builder) => {
    builder.addCase(fetchUserById.pending, (state, action) => {
      // both `state` and `action` are now correctly typed
      // based on the slice state and the `pending` action creator
    })
  },
})
```

Like the `builder` in `createReducer`, this `builder` also accepts `addMatcher` (see [typing `builder.matcher`](#typing-builderaddmatcher)) and `addDefaultCase`.

### Payload with All Optional Fields

If you try to supply a payload type where all fields are optional, like `PayloadAction<Partial<User>>` or `PayloadAction<{value?: string}>`, TS may not be able to infer the action type correctly.

You can work around this by [using a custom `AtLeastOne` utility type](https://github.com/reduxjs/redux-toolkit/issues/1423#issuecomment-902680573) to help ensure that at least one of the fields must be passed in:

```ts no-transpile
type AtLeastOne<T extends Record<string, any>> = keyof T extends infer K
  ? K extends string
    ? Pick<T, K & keyof T> & Partial<T>
    : never
  : never

// Use this type instead of `Partial<MyPayloadType>`
type AtLeastOneUserField = AtLeastOne<User>
```

### Typing Async Thunks Inside `createSlice`

As of 2.0, `createSlice` allows [defining thunks inside of `reducers` using a callback syntax](../api/createSlice.mdx#the-reducers-creator-callback-notation).

Typing for the `create.asyncThunk` method works in the same way as [`createAsyncThunk`](#createasyncthunk), with one key difference.

A type for `state` and/or `dispatch` _cannot_ be provided as part of the `ThunkApiConfig`, as this would cause circular types.

Instead, it is necessary to assert the type when needed - `getState() as RootState`. You may also include an explicit return type for the payload function as well, in order to break the circular type inference cycle.

```ts no-transpile
create.asyncThunk<Todo, string, { rejectValue: { error: string } }>(
  // highlight-start
  // may need to include an explicit return type
  async (id: string, thunkApi): Promise<Todo> => {
    // Cast types for `getState` and `dispatch` manually
    const state = thunkApi.getState() as RootState
    const dispatch = thunkApi.dispatch as AppDispatch
    // highlight-end
    try {
      const todo = await fetchTodo()
      return todo
    } catch (e) {
      throw thunkApi.rejectWithValue({
        error: 'Oh no!',
      })
    }
  },
)
```

For common thunk API configuration options, a [`withTypes` helper](../usage/usage-with-typescript#defining-a-pre-typed-createasyncthunk) is provided:

```ts no-transpile
reducers: (create) => {
  const createAThunk = create.asyncThunk.withTypes<{
    rejectValue: { error: string }
  }>()

  return {
    fetchTodo: createAThunk<Todo, string>(async (id, thunkApi) => {
      throw thunkApi.rejectWithValue({
        error: 'Oh no!',
      })
    }),
    fetchTodos: createAThunk<Todo[], string>(async (id, thunkApi) => {
      throw thunkApi.rejectWithValue({
        error: 'Oh no, not again!',
      })
    }),
  }
}
```

### Wrapping `createSlice`

If you need to reuse reducer logic, it is common to write ["higher-order reducers"](/usage/structuring-reducers/reusing-reducer-logic#customizing-behavior-with-higher-order-reducers) that wrap a reducer function with additional common behavior. This can be done with `createSlice` as well, but due to the complexity of the types for `createSlice`, you have to use the `SliceCaseReducers` and `ValidateSliceCaseReducers` types in a very specific way.

Here is an example of such a "generic" wrapped `createSlice` call:

```ts
interface GenericState<T> {
  data?: T
  status: 'loading' | 'finished' | 'error'
}

const createGenericSlice = <
  T,
  Reducers extends SliceCaseReducers<GenericState<T>>,
>({
  name = '',
  initialState,
  reducers,
}: {
  name: string
  initialState: GenericState<T>
  reducers: ValidateSliceCaseReducers<GenericState<T>, Reducers>
}) => {
  return createSlice({
    name,
    initialState,
    reducers: {
      start(state) {
        state.status = 'loading'
      },
      /**
       * If you want to write to values of the state that depend on the generic
       * (in this case: `state.data`, which is T), you might need to specify the
       * State type manually here, as it defaults to `Draft<GenericState<T>>`,
       * which can sometimes be problematic with yet-unresolved generics.
       * This is a general problem when working with immer's Draft type and generics.
       */
      success(state: GenericState<T>, action: PayloadAction<T>) {
        state.data = action.payload
        state.status = 'finished'
      },
      ...reducers,
    },
  })
}

const wrappedSlice = createGenericSlice({
  name: 'test',
  initialState: { status: 'loading' } as GenericState<string>,
  reducers: {
    magic(state) {
      state.status = 'finished'
      state.data = 'hocus pocus'
    },
  },
})
```

## `createAsyncThunk`

### Basic `createAsyncThunk` Types

In the most common use cases, you should not need to explicitly declare any types for the `createAsyncThunk` call itself.

Just provide a type for the first argument to the `payloadCreator` argument as you would for any function argument, and the resulting thunk will accept the same type as its input parameter.
The return type of the `payloadCreator` will also be reflected in all generated action types.

```ts
interface MyData {
  // ...
}

const fetchUserById = createAsyncThunk(
  'users/fetchById',
  // highlight-start
  // Declare the type your function argument here:
  async (userId: number) => {
    // highlight-end
    const response = await fetch(`https://reqres.in/api/users/${userId}`)
    // Inferred return type: Promise<MyData>
    // highlight-next-line
    return (await response.json()) as MyData
  },
)

// the parameter of `fetchUserById` is automatically inferred to `number` here
// and dispatching the resulting thunkAction will return a Promise of a correctly
// typed "fulfilled" or "rejected" action.
const lastReturnedAction = await store.dispatch(fetchUserById(3))
```

### Handling responses from async thunks

The preferred approach to handling responses from thunks is via the `unwrap` method (see [Unwrapping Result Actions](../api/createAsyncThunk.mdx#unwrapping-result-actions)).

```ts
const handleClick = async (userData) => {
  try {
    const result = await dispatch(updateUser(userData)).unwrap()
    showToast('success', `Updated ${result.name}`)
  } catch (error) {
    showToast('error', `Update failed: ${error.message}`)
  }
}
```

### Typing the `thunkApi` Object

The second argument to the `payloadCreator`, known as `thunkApi`, is an object containing references to the `dispatch`, `getState`, and `extra` arguments from the thunk middleware as well as a utility function called `rejectWithValue`. If you want to use these from within the `payloadCreator`, you will need to define some generic arguments, as the types for these arguments cannot be inferred. Also, as TS cannot mix explicit and inferred generic parameters, from this point on you'll have to define the `Returned` and `ThunkArg` generic parameter as well.

#### Manually Defining `thunkApi` Types

To define the types for these arguments, pass an object as the third generic argument, with type declarations for some or all of these fields:

```ts
type AsyncThunkConfig = {
  /** return type for `thunkApi.getState` */
  state?: unknown
  /** type for `thunkApi.dispatch` */
  dispatch?: Dispatch
  /** type of the `extra` argument for the thunk middleware, which will be passed in as `thunkApi.extra` */
  extra?: unknown
  /** type to be passed into `rejectWithValue`'s first argument that will end up on `rejectedAction.payload` */
  rejectValue?: unknown
  /** return type of the `serializeError` option callback */
  serializedErrorType?: unknown
  /** type to be returned from the `getPendingMeta` option callback & merged into `pendingAction.meta` */
  pendingMeta?: unknown
  /** type to be passed into the second argument of `fulfillWithValue` to finally be merged into `fulfilledAction.meta` */
  fulfilledMeta?: unknown
  /** type to be passed into the second argument of `rejectWithValue` to finally be merged into `rejectedAction.meta` */
  rejectedMeta?: unknown
}
```

```ts
const fetchUserById = createAsyncThunk<
  // highlight-start
  // Return type of the payload creator
  MyData,
  // First argument to the payload creator
  number,
  {
    // Optional fields for defining thunkApi field types
    dispatch: AppDispatch
    state: State
    extra: {
      jwt: string
    }
  }
  // highlight-end
>('users/fetchById', async (userId, thunkApi) => {
  const response = await fetch(`https://reqres.in/api/users/${userId}`, {
    headers: {
      Authorization: `Bearer ${thunkApi.extra.jwt}`,
    },
  })
  return (await response.json()) as MyData
})
```

If you are performing a request that you know will typically either be a success or have an expected error format, you can pass in a type to `rejectValue` and `return rejectWithValue(knownPayload)` in the action creator. This allows you to reference the error payload in the reducer as well as in a component after dispatching the `createAsyncThunk` action.

```ts
interface MyKnownError {
  errorMessage: string
  // ...
}
interface UserAttributes {
  id: string
  first_name: string
  last_name: string
  email: string
}

const updateUser = createAsyncThunk<
  // Return type of the payload creator
  MyData,
  // First argument to the payload creator
  UserAttributes,
  // Types for ThunkAPI
  {
    extra: {
      jwt: string
    }
    rejectValue: MyKnownError
  }
>('users/update', async (user, thunkApi) => {
  const { id, ...userData } = user
  const response = await fetch(`https://reqres.in/api/users/${id}`, {
    method: 'PUT',
    headers: {
      Authorization: `Bearer ${thunkApi.extra.jwt}`,
    },
    body: JSON.stringify(userData),
  })
  if (response.status === 400) {
    // Return the known error for future handling
    return thunkApi.rejectWithValue((await response.json()) as MyKnownError)
  }
  return (await response.json()) as MyData
})
```

While this notation for `state`, `dispatch`, `extra` and `rejectValue` might seem uncommon at first, it allows you to provide only the types for these you actually need - so for example, if you are not accessing `getState` within your `payloadCreator`, there is no need to provide a type for `state`. The same can be said about `rejectValue` - if you don't need to access any potential error payload, you can ignore it.

In addition, you can leverage checks against `action.payload` and `match` as provided by `createAction` as a type-guard for when you want to access known properties on defined types. Example:

- In a reducer

```ts
const usersSlice = createSlice({
  name: 'users',
  initialState: {
    entities: {},
    error: null,
  },
  reducers: {},
  extraReducers: (builder) => {
    builder.addCase(updateUser.fulfilled, (state, { payload }) => {
      state.entities[payload.id] = payload
    })
    builder.addCase(updateUser.rejected, (state, action) => {
      if (action.payload) {
        // Since we passed in `MyKnownError` to `rejectValue` in `updateUser`, the type information will be available here.
        state.error = action.payload.errorMessage
      } else {
        state.error = action.error
      }
    })
  },
})
```

- In a component

```ts
const handleUpdateUser = async (userData) => {
  const resultAction = await dispatch(updateUser(userData))
  if (updateUser.fulfilled.match(resultAction)) {
    const user = resultAction.payload
    showToast('success', `Updated ${user.name}`)
  } else {
    if (resultAction.payload) {
      // Since we passed in `MyKnownError` to `rejectValue` in `updateUser`, the type information will be available here.
      // Note: this would also be a good place to do any handling that relies on the `rejectedWithValue` payload, such as setting field errors
      showToast('error', `Update failed: ${resultAction.payload.errorMessage}`)
    } else {
      showToast('error', `Update failed: ${resultAction.error.message}`)
    }
  }
}
```

### Defining a Pre-Typed `createAsyncThunk`

As of RTK 1.9, you can define a "pre-typed" version of `createAsyncThunk` that can have the types for `state`, `dispatch`, and `extra` built in. This lets you set up those types once, so you don't have to repeat them each time you call `createAsyncThunk`.

To do this, call `createAsyncThunk.withTypes<>()`, and pass in an object containing the field names and types for any of the fields in the `AsyncThunkConfig` type listed above. This might look like:

```ts
const createAppAsyncThunk = createAsyncThunk.withTypes<{
  state: RootState
  dispatch: AppDispatch
  rejectValue: string
  extra: { s: string; n: number }
}>()
```

Import and use that pre-typed `createAppAsyncThunk` instead of the original, and the types will be used automatically.

## `createEntityAdapter`

Usage of `createEntityAdapter` with TypeScript varies based on whether your entities are normalized by an `id` property, or whether a custom `selectId` is needed.

If your entities are normalized by an `id` property, `createEntityAdapter` only requires you to specify the entity type as the single generic argument. For example:

```ts
interface Book {
  id: number
  title: string
}

// no selectId needed here, as the entity has an `id` property we can default to
// highlight-next-line
const booksAdapter = createEntityAdapter<Book>({
  sortComparer: (a, b) => a.title.localeCompare(b.title),
})

const booksSlice = createSlice({
  name: 'books',
  initialState: booksAdapter.getInitialState(),
  reducers: {
    bookAdded: booksAdapter.addOne,
    booksReceived(state, action: PayloadAction<{ books: Book[] }>) {
      booksAdapter.setAll(state, action.payload.books)
    },
  },
})
```

On the other hand, if the entity needs to be normalized by a different property, we instead recommend passing a custom `selectId` function and annotating there. This allows proper inference of the ID's type, instead of having to provide it manually.

```ts
interface Book {
  bookId: number
  title: string
  // ...
}

const booksAdapter = createEntityAdapter({
  // highlight-next-line
  selectId: (book: Book) => book.bookId,
  sortComparer: (a, b) => a.title.localeCompare(b.title),
})

const booksSlice = createSlice({
  name: 'books',
  initialState: booksAdapter.getInitialState(),
  reducers: {
    bookAdded: booksAdapter.addOne,
    booksReceived(state, action: PayloadAction<{ books: Book[] }>) {
      booksAdapter.setAll(state, action.payload.books)
    },
  },
})
```

### Using `createEntityAdapter` with `normalizr`

When using a library like [`normalizr`](https://github.com/paularmstrong/normalizr/), your normalized data will resemble this shape:

```js
{
  result: 1,
  entities: {
    1: { id: 1, other: 'property' },
    2: { id: 2, other: 'property' }
  }
}
```

The methods `addMany`, `upsertMany`, and `setAll` all allow you to pass in the `entities` portion of this directly with no extra conversion steps. However, the `normalizr` TS typings currently do not correctly reflect that multiple data types may be included in the results, so you will need to specify that type structure yourself.

Here is an example of how that would look:

```ts
type Author = { id: number; name: string }
type Article = { id: number; title: string }
type Comment = { id: number; commenter: number }

export const fetchArticle = createAsyncThunk(
  'articles/fetchArticle',
  async (id: number) => {
    const data = await fakeAPI.articles.show(id)
    // Normalize the data so reducers can responded to a predictable payload.
    // Note: at the time of writing, normalizr does not automatically infer the result,
    // so we explicitly declare the shape of the returned normalized data as a generic arg.
    const normalized = normalize<
      any,
      {
        articles: { [key: string]: Article }
        users: { [key: string]: Author }
        comments: { [key: string]: Comment }
      }
    >(data, articleEntity)
    return normalized.entities
  },
)

export const slice = createSlice({
  name: 'articles',
  initialState: articlesAdapter.getInitialState(),
  reducers: {},
  extraReducers: (builder) => {
    builder.addCase(fetchArticle.fulfilled, (state, action) => {
      // The type signature on action.payload matches what we passed into the generic for `normalize`, allowing us to access specific properties on `payload.articles` if desired
      articlesAdapter.upsertMany(state, action.payload.articles)
    })
  },
})
```

---

### Rtk Query Api ApiProvider (`docs/rtk-query/api/ApiProvider.mdx`)

---
id: ApiProvider
title: ApiProvider
sidebar_label: ApiProvider
hide_title: true
description: 'RTK Query > API: ApiProvider reference'
---

&nbsp;

# `ApiProvider`

[summary](docblock://query/react/ApiProvider.tsx?token=ApiProvider)

[examples](docblock://query/react/ApiProvider.tsx?token=ApiProvider)

:::danger
Using this together with an existing Redux store will cause them to conflict with each other. If you are already using Redux, please follow the instructions as shown in the [Getting Started guide](../../introduction/getting-started).
:::

---

### Rtk Query Api CreateApi (`docs/rtk-query/api/createApi.mdx`)

---
id: createApi
title: createApi
sidebar_label: createApi
hide_title: true
description: 'RTK Query > API: createApi reference'
---

&nbsp;

# `createApi`

`createApi` is the core of RTK Query's functionality. It allows you to define a set of "endpoints" that describe how to retrieve data from backend APIs and other async sources, including the configuration of how to fetch and transform that data. It generates [an "API slice" structure](./created-api/overview.mdx) that contains Redux logic (and optionally React hooks) that encapsulate the data fetching and caching process for you.

:::tip

Typically, you should only have one API slice per base URL that your application needs to communicate with. For example, if your site fetches data from both `/api/posts` and `/api/users`, you would have a single API slice with `/api/` as the base URL, and separate endpoint definitions for `posts` and `users`. This allows you to effectively take advantage of [automated re-fetching](../usage/automated-refetching.mdx) by defining [tag](../usage/automated-refetching.mdx#tags) relationships across endpoints.

This is because:

- Automatic tag invalidation only works within a single API slice. If you have multiple API slices, the automatic invalidation won't work across them.
- Every `createApi` call generates its own middleware, and each middleware added to the store will run checks against every dispatched action. That has a perf cost that adds up. So, if you called `createApi` 10 times and added 10 separate API middleware to the store, that will be noticeably slower perf-wise.

For maintainability purposes, you may wish to split up endpoint definitions across multiple files, while still maintaining a single API slice which includes all of these endpoints. See [code splitting](../usage/code-splitting.mdx) for how you can use the `injectEndpoints` property to inject API endpoints from other files into a single API slice definition.

:::

```ts title="Example: src/services/pokemon.ts"
// file: src/services/types.ts noEmit
export type Pokemon = {}

// file: src/services/pokemon.ts
// Need to use the React-specific entry point to allow generating React hooks
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'
import type { Pokemon } from './types'

// highlight-start
// Define a service using a base URL and expected endpoints
export const pokemonApi = createApi({
  reducerPath: 'pokemonApi',
  baseQuery: fetchBaseQuery({ baseUrl: 'https://pokeapi.co/api/v2/' }),
  endpoints: (build) => ({
    getPokemonByName: build.query<Pokemon, string>({
      query: (name) => `pokemon/${name}`,
    }),
  }),
})
//highlight-end

// highlight-start
// Export hooks for usage in function components, which are
// auto-generated based on the defined endpoints
export const { useGetPokemonByNameQuery } = pokemonApi
// highlight-end
```

## `createApi` Parameters

`createApi` accepts a single configuration object parameter with the following options:

```ts no-transpile
  baseQuery(args: InternalQueryArgs, api: BaseQueryApi, extraOptions?: DefinitionExtraOptions): any;
  endpoints(build: EndpointBuilder<InternalQueryArgs, TagTypes>): Definitions;
  extractRehydrationInfo?: (
    action: UnknownAction,
    {
      reducerPath,
    }: {
      reducerPath: ReducerPath
    }
  ) =>
    | undefined
    | CombinedState<Definitions, TagTypes, ReducerPath>
  tagTypes?: readonly TagTypes[];
  reducerPath?: ReducerPath;
  serializeQueryArgs?: SerializeQueryArgs<InternalQueryArgs>;
  keepUnusedDataFor?: number; // value is in seconds
  refetchOnMountOrArgChange?: boolean | number; // value is in seconds
  refetchOnFocus?: boolean;
  refetchOnReconnect?: boolean;
```

### `baseQuery`

[summary](docblock://query/createApi.ts?token=CreateApiOptions.baseQuery)

#### baseQuery function arguments

- `args` - The return value of the `query` function for a given endpoint
- `api` - The `BaseQueryApi` object contains:
  - `signal` - An [`AbortSignal`](https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal) object that may be used to abort DOM requests and/or read whether the request is aborted.
  - `abort` - The [`abort()`](https://developer.mozilla.org/en-US/docs/Web/API/AbortController/abort) method of the AbortController attached to `signal`.
  - `dispatch` - The `store.dispatch` method for the corresponding Redux store
  - `getState` - A function that may be called to access the current store state
  - `extra` - Provided as thunk.extraArgument to the configureStore getDefaultMiddleware option.
  - `endpoint` - The name of the endpoint.
  - `type` - Type of request (`query` or `mutation`).
  - `forced` - Indicates if a query has been forced.
  - `queryCacheKey`- The computed query cache key.
- `extraOptions` - The value of the optional `extraOptions` property provided for a given endpoint

#### baseQuery function signature

```ts title="Base Query signature" no-transpile
export type BaseQueryFn<
  Args = any,
  Result = unknown,
  Error = unknown,
  DefinitionExtraOptions = {},
  Meta = {},
> = (
  args: Args,
  api: BaseQueryApi,
  extraOptions: DefinitionExtraOptions,
) => MaybePromise<QueryReturnValue<Result, Error, Meta>>

export interface BaseQueryApi {
  signal: AbortSignal
  abort: (reason?: string) => void
  dispatch: ThunkDispatch<any, any, any>
  getState: () => unknown
  extra: unknown
  endpoint: string
  type: 'query' | 'mutation'
  forced?: boolean
}

export type QueryReturnValue<T = unknown, E = unknown, M = unknown> =
  | {
      error: E
      data?: undefined
      meta?: M
    }
  | {
      error?: undefined
      data: T
      meta?: M
    }
```

[examples](docblock://query/createApi.ts?token=CreateApiOptions.baseQuery)

### `endpoints`

[summary](docblock://query/createApi.ts?token=CreateApiOptions.endpoints)

See [Endpoint Definition Parameters](#endpoint-definition-parameters) for details on individual properties.

#### Query endpoint definition

Query endpoints (defined with `build.query()`) are used to cache data fetched from the server.

You must specify either a `query` field (which will use the API's `baseQuery` to make a request), or a `queryFn` function with your own async logic. All other fields are optional.

```
/* Detailed source-code truncated for AI context efficiency. */
```

#### Infinite Query endpoint definition

Infinite query endpoints (defined with `build.infiniteQuery()`) are used to cache multi-page data sets from the server. They have all the same callbacks and options as standard query endpoints, but also require an additional [`infiniteQueryOptions`](#infinitequeryoptions) field to specify how to calculate the unique parameters to fetch each page.

For infinite query endpoints, there is a separation between the "query arg" used for the cache key, and the "page param" used to fetch a specific page. For example, a Pokemon API endpoint might have a string query arg like `"fire"` , but use a page number as the param to determine which page to fetch out of the results. The `query` and `queryFn` methods will receive a combined `{queryArg, pageParam}` object as the argument, rather than just the `queryArg` by itself.

```
/* Detailed source-code truncated for AI context efficiency. */
```

#### Mutation endpoint definition

Mutation endpoints (defined with `build.mutation()`) are used to send updates to the server, and force invalidation and refetching of query endpoints.

As with queries, you must specify either the `query` option or the `queryFn` async method.

```
/* Detailed source-code truncated for AI context efficiency. */
```

#### How endpoints get used

When defining a key like `getPosts` as shown below, it's important to know that this name will become exportable from `api` and be able to referenced under `api.endpoints.getPosts.useQuery()`, `api.endpoints.getPosts.initiate()` and `api.endpoints.getPosts.select()`. The same thing applies to `mutation`s but they reference `useMutation` instead of `useQuery`.

```ts
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'
interface Post {
  id: number
  name: string
}
type PostsResponse = Post[]

const api = createApi({
  baseQuery: fetchBaseQuery({ baseUrl: '/' }),
  tagTypes: ['Posts'],
  endpoints: (build) => ({
    getPosts: build.query<PostsResponse, void>({
      query: () => 'posts',
      providesTags: (result) =>
        result ? result.map(({ id }) => ({ type: 'Posts', id })) : [],
    }),
    addPost: build.mutation<Post, Partial<Post>>({
      query: (body) => ({
        url: `posts`,
        method: 'POST',
        body,
      }),
      invalidatesTags: ['Posts'],
    }),
  }),
})

// Auto-generated hooks
export const { useGetPostsQuery, useAddPostMutation } = api

// Possible exports
export const { endpoints, reducerPath, reducer, middleware } = api
// reducerPath, reducer, middleware are only used in store configuration
// endpoints will have:
// endpoints.getPosts.initiate(), endpoints.getPosts.select(), endpoints.getPosts.useQuery()
// endpoints.addPost.initiate(), endpoints.addPost.select(), endpoints.addPost.useMutation()
// see `createApi` overview for _all exports_
```

### `extractRehydrationInfo`

[summary](docblock://query/createApi.ts?token=CreateApiOptions.extractRehydrationInfo)

[examples](docblock://query/createApi.ts?token=CreateApiOptions.extractRehydrationInfo)

See also [Server Side Rendering](../usage/server-side-rendering.mdx) and
[Persistence and Rehydration](../usage/persistence-and-rehydration.mdx).

### `tagTypes`

[summary](docblock://query/createApi.ts?token=CreateApiOptions.tagTypes)

[examples](docblock://query/createApi.ts?token=CreateApiOptions.tagTypes)

### `reducerPath`

[summary](docblock://query/createApi.ts?token=CreateApiOptions.reducerPath)

[examples](docblock://query/createApi.ts?token=CreateApiOptions.reducerPath)

### `serializeQueryArgs`

[summary](docblock://query/createApi.ts?token=CreateApiOptions.serializeQueryArgs)

By default, this function will take the query arguments, sort object keys where applicable, stringify the result, and concatenate it with the endpoint name. This creates a cache key based on the combination of arguments + endpoint name (ignoring object key order), such that calling any given endpoint with the same arguments will result in the same cache key.

### `invalidationBehavior`

[summary](docblock://query/createApi.ts?token=CreateApiOptions.invalidationBehavior)

### `keepUnusedDataFor`

[summary](docblock://query/createApi.ts?token=CreateApiOptions.keepUnusedDataFor)

[examples](docblock://query/createApi.ts?token=CreateApiOptions.keepUnusedDataFor)

### `refetchOnMountOrArgChange`

[summary](docblock://query/createApi.ts?token=CreateApiOptions.refetchOnMountOrArgChange)

:::note
You can set this globally in `createApi`, but you can also override the default value and have more granular control by passing `refetchOnMountOrArgChange` to each individual hook call or similarly by passing `forceRefetch: true` when dispatching the [`initiate`](./created-api/endpoints.mdx#initiate) action.
:::

### `refetchOnFocus`

[summary](docblock://query/createApi.ts?token=CreateApiOptions.refetchOnFocus)

:::note
You can set this globally in `createApi`, but you can also override the default value and have more granular control by passing `refetchOnFocus` to each individual hook call or when dispatching the [`initiate`](./created-api/endpoints.mdx#initiate) action.

If you specify `track: false` when manually dispatching queries, RTK Query will not be able to automatically refetch for you.
:::

### `refetchOnReconnect`

[summary](docblock://query/createApi.ts?token=CreateApiOptions.refetchOnReconnect)

:::note
You can set this globally in `createApi`, but you can also override the default value and have more granular control by passing `refetchOnReconnect` to each individual hook call or when dispatching the [`initiate`](./created-api/endpoints.mdx#initiate) action.

If you specify `track: false` when manually dispatching queries, RTK Query will not be able to automatically refetch for you.
:::

### `onSchemaFailure`

[summary](docblock://query/createApi.ts?token=CreateApiOptions.onSchemaFailure)

[examples](docblock://query/createApi.ts?token=CreateApiOptions.onSchemaFailure)

:::note
You can set this globally in `createApi`, but you can also override the default value and have more granular control by passing `onSchemaFailure` to each individual endpoint definition.
:::

### `catchSchemaFailure`

[summary](docblock://query/createApi.ts?token=CreateApiOptions.catchSchemaFailure)

[examples](docblock://query/createApi.ts?token=CreateApiOptions.catchSchemaFailure)

:::note
You can set this globally in `createApi`, but you can also override the default value and have more granular control by passing `catchSchemaFailure` to each individual endpoint definition.
:::

### `skipSchemaValidation`

[summary](docblock://query/createApi.ts?token=CreateApiOptions.skipSchemaValidation)

[examples](docblock://query/createApi.ts?token=CreateApiOptions.skipSchemaValidation)

:::note
You can set this globally in `createApi`, but you can also override the default value and have more granular control by passing `skipSchemaValidation` to each individual endpoint definition.
:::

## Endpoint Definition Parameters

### `query`

_(required if no `queryFn` provided)_

```ts title="query signature" no-transpile
export type query = <QueryArg>(
  arg: QueryArg,
) => string | Record<string, unknown>

// with `fetchBaseQuery`
export type query = <QueryArg>(arg: QueryArg) => string | FetchArgs
```

[summary](docblock://query/endpointDefinitions.ts?token=EndpointDefinitionWithQuery.query)

[examples](docblock://query/endpointDefinitions.ts?token=EndpointDefinitionWithQuery.query)

### `queryFn`

_(required if no `query` provided)_

[summary](docblock://query/endpointDefinitions.ts?token=EndpointDefinitionWithQueryFn.queryFn)

Called with the same arguments as `baseQuery`, as well as the provided `baseQuery` function itself. It is expected to return an object with either a `data` or `error` property, or a promise that resolves to return such an object.

See also [Customizing queries with queryFn](../usage/customizing-queries.mdx#customizing-queries-with-queryfn).

```ts title="queryFn signature" no-transpile
queryFn(
  arg: QueryArg,
  api: BaseQueryApi,
  extraOptions: BaseQueryExtraOptions<BaseQuery>,
  baseQuery: (arg: Parameters<BaseQuery>[0]) => ReturnType<BaseQuery>
): MaybePromise<
| {
    error: BaseQueryError<BaseQuery>
    data?: undefined
  }
| {
    error?: undefined
    data: ResultType
  }
>

export interface BaseQueryApi {
  signal: AbortSignal
  dispatch: ThunkDispatch<any, any, any>
  getState: () => unknown
}
```

#### `queryFn` function arguments

- `args` - The argument provided when the query itself is called
- `api` - The `BaseQueryApi` object, containing `signal`, `dispatch` and `getState` properties
  - `signal` - An [`AbortSignal`](https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal) object that may be used to abort DOM requests and/or read whether the request is aborted.
  - `dispatch` - The `store.dispatch` method for the corresponding Redux store
  - `getState` - A function that may be called to access the current store state
- `extraOptions` - The value of the optional `extraOptions` property provided for the endpoint
- `baseQuery` - The `baseQuery` function provided to the api itself

[examples](docblock://query/endpointDefinitions.ts?token=EndpointDefinitionWithQueryFn.queryFn)

### `infiniteQueryOptions`

_(only for `infiniteQuery` endpoints)_

[summary](docblock://query/endpointDefinitions.ts?token=InfiniteQueryExtraOptions.infiniteQueryOptions)

The `infiniteQueryOptions` field includes:

- `initialPageParam`: the default page param value used for the first request, if this was not specified at the usage site
- `getNextPageParam`: a required callback you must provide to calculate the next page param, given the existing cached pages and page params
- `getPreviousPageParam`: an optional callback that will be used to calculate the previous page param, if you try to fetch backwards.
- `maxPages`: an optional limit to how many fetched pages will be kept in the cache entry at a time

[examples](docblock://query/endpointDefinitions.ts?token=InfiniteQueryExtraOptions.infiniteQueryOptions)

### `transformResponse`

_(optional, not applicable with `queryFn`)_

[summary](docblock://query/endpointDefinitions.ts?token=EndpointDefinitionWithQuery.transformResponse)

In some cases, you may want to manipulate the data returned from a query before you put it in the cache. In this instance, you can take advantage of `transformResponse`.

See also [Customizing query responses with `transformResponse`](../usage/customizing-queries.mdx#customizing-query-responses-with-transformresponse)

```ts title="Unpack a deeply nested collection" no-transpile
transformResponse: (response, meta, arg) =>
  response.some.deeply.nested.collection
```

### `transformErrorResponse`

_(optional, not applicable with `queryFn`)_

[summary](docblock://query/endpointDefinitions.ts?token=EndpointDefinitionWithQuery.transformErrorResponse)

In some cases, you may want to manipulate the error returned from a query before you put it in the cache. In this instance, you can take advantage of `transformErrorResponse`.

See also [Customizing query responses with `transformErrorResponse`](../usage/customizing-queries.mdx#customizing-query-responses-with-transformerrorresponse)

```ts title="Unpack a deeply nested error object" no-transpile
transformErrorResponse: (response, meta, arg) =>
  response.data.some.deeply.nested.errorObject
```

### `extraOptions`

_(optional)_

Passed as the third argument to the supplied `baseQuery` function

### `providesTags`

_(optional, only for query endpoints)_

[summary](docblock://query/endpointDefinitions.ts?token=QueryExtraOptions.providesTags)

See also [Providing cache data](../usage/automated-refetching.mdx#providing-cache-data).

[examples](docblock://query/endpointDefinitions.ts?token=QueryExtraOptions.providesTags)

### `invalidatesTags`

_(optional, only for mutation endpoints)_

[summary](docblock://query/endpointDefinitions.ts?token=MutationExtraOptions.invalidatesTags)

See also [Invalidating cache data](../usage/automated-refetching.mdx#invalidating-cache-data).

[examples](docblock://query/endpointDefinitions.ts?token=MutationExtraOptions.invalidatesTags)

### `keepUnusedDataFor`

_(optional, only for query endpoints)_

Overrides the api-wide definition of `keepUnusedDataFor` for this endpoint only.

[summary](docblock://query/createApi.ts?token=CreateApiOptions.keepUnusedDataFor)

[examples](docblock://query/core/buildMiddleware/cacheCollection.ts?token=CacheCollectionQueryExtraOptions)

### `serializeQueryArgs`

_(optional, only for query endpoints)_

[summary](docblock://query/endpointDefinitions.ts?token=QueryExtraOptions.serializeQueryArgs)

[examples](docblock://query/endpointDefinitions.ts?token=QueryExtraOptions.serializeQueryArgs)

### `merge`

_(optional, only for query endpoints)_

[summary](docblock://query/endpointDefinitions.ts?token=QueryExtraOptions.merge)

[examples](docblock://query/endpointDefinitions.ts?token=QueryExtraOptions.merge)

### `forceRefetch`

_(optional, only for query endpoints)_

```ts title="forceRefetch signature" no-transpile
type forceRefetch = (params: {
  currentArg: QueryArg | undefined
  previousArg: QueryArg | undefined
  state: RootState<any, any, string>
  endpointState?: QuerySubState<any>
}) => boolean
```

[summary](docblock://query/endpointDefinitions.ts?token=QueryExtraOptions.forceRefetch)

[examples](docblock://query/endpointDefinitions.ts?token=QueryExtraOptions.forceRefetch)

### `onQueryStarted`

_(optional)_

Available to both [queries](../usage/queries.mdx) and [mutations](../usage/mutations.mdx).

A function that is called when you start each individual query or mutation. The function is called with a lifecycle api object containing properties such as `queryFulfilled`, allowing code to be run when a query is started, when it succeeds, and when it fails (i.e. throughout the lifecycle of an individual query/mutation call).

Can be used in `mutations` for [optimistic updates](../usage/manual-cache-updates.mdx#optimistic-updates).

#### Lifecycle API properties

- `dispatch` - The dispatch method for the store.
- `getState` - A method to get the current state for the store.
- `extra` - `extra` as provided as `thunk.extraArgument` to the `configureStore` `getDefaultMiddleware` option.
- `requestId` - A unique ID generated for the query/mutation.
- `queryFulfilled` - A Promise that will resolve with a `data` property (the transformed query result),
  and a `meta` property (`meta` returned by the `baseQuery`).
  If the query fails, this Promise will reject with the error. This allows you to `await` for the query to finish.
- `getCacheEntry` - A function that gets the current value of the cache entry.
- `updateCachedData` _(query endpoints only)_ - A function that accepts a 'recipe' callback specifying how to update the data for the corresponding cache at the time it is called. This uses `immer` internally, and updates can be written 'mutably' while safely producing the next immutable state.

```ts title="Mutation onQueryStarted signature" no-transpile
async function onQueryStarted(
  arg: QueryArg,
  {
    dispatch,
    getState,
    extra,
    requestId,
    queryFulfilled,
    getCacheEntry,
  }: MutationLifecycleApi,
): Promise<void>
```

```ts title="Query onQueryStarted signature" no-transpile
async function onQueryStarted(
  arg: QueryArg,
  {
    dispatch,
    getState,
    extra,
    requestId,
    queryFulfilled,
    getCacheEntry,
    updateCachedData, // available for query endpoints only
  }: QueryLifecycleApi,
): Promise<void>
```

```ts title="onQueryStarted query lifecycle example"
// file: notificationsSlice.ts noEmit
export const messageCreated = (msg: string) => ({
  type: 'notifications/messageCreated',
  payload: msg,
})

// file: api.ts
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query'
import { messageCreated } from './notificationsSlice'

export interface Post {
  id: number
  name: string
}

const api = createApi({
  baseQuery: fetchBaseQuery({
    baseUrl: '/',
  }),
  endpoints: (build) => ({
    getPost: build.query<Post, number>({
      query: (id) => `post/${id}`,
      async onQueryStarted(id, { dispatch, queryFulfilled }) {
        // `onStart` side-effect
        dispatch(messageCreated('Fetching post...'))
        try {
          const { data } = await queryFulfilled
          // `onSuccess` side-effect
          dispatch(messageCreated('Post received!'))
        } catch (err) {
          // `onError` side-effect
          dispatch(messageCreated('Error fetching post!'))
        }
      },
    }),
  }),
})
```

### `onCacheEntryAdded`

_(optional)_

Available to both [queries](../usage/queries.mdx) and [mutations](../usage/mutations.mdx).

A function that is called when a new cache entry is added, i.e. when a new subscription for the endpoint + query parameters combination is created. The function is called with a lifecycle api object containing properties such as `cacheDataLoaded` & `cacheDataRemoved`, allowing code to be run when a cache entry is added, when cache data is loaded, and when the cache entry is removed (i.e. throughout the lifecycle of a cache entry).

Can be used for [streaming updates](../usage/streaming-updates.mdx).

#### Cache Lifecycle API properties

- `dispatch` - The dispatch method for the store.
- `getState` - A method to get the current state for the store.
- `extra` - `extra` as provided as `thunk.extraArgument` to the `configureStore` `getDefaultMiddleware` option.
- `requestId` - A unique ID generated for the cache entry.
- `cacheEntryRemoved` - A Promise that allows you to wait for the point in time when the cache entry has been removed from the cache, by not being used/subscribed to any more in the application for too long or by dispatching `api.util.resetApiState`.
- `cacheDataLoaded` - A Promise that will resolve with the first value for this cache key. This allows you to `await` until an actual value is in the cache.
  Note: If the cache entry is removed from the cache before any value has ever been resolved, this Promise will reject with `new Error('Promise never resolved before cacheEntryRemoved.')` to prevent memory leaks. You can just re-throw that error (or not handle it at all) - it will be caught outside of `cacheEntryAdded`.
- `getCacheEntry` - A function that gets the current value of the cache entry.
- `updateCachedData` _(query endpoints only)_ - A function that accepts a 'recipe' callback specifying how to update the data at the time it is called. This uses `immer` internally, and updates can be written 'mutably' while safely producing the next immutable state.

```ts title="Mutation onCacheEntryAdded signature" no-transpile
async function onCacheEntryAdded(
  arg: QueryArg,
  {
    dispatch,
    getState,
    extra,
    requestId,
    cacheEntryRemoved,
    cacheDataLoaded,
    getCacheEntry,
  }: MutationCacheLifecycleApi,
): Promise<void>
```

```ts title="Query onCacheEntryAdded signature" no-transpile
async function onCacheEntryAdded(
  arg: QueryArg,
  {
    dispatch,
    getState,
    extra,
    requestId,
    cacheEntryRemoved,
    cacheDataLoaded,
    getCacheEntry,
    updateCachedData, // available for query endpoints only
  }: QueryCacheLifecycleApi,
): Promise<void>
```

### Schema Validation

Endpoints can have schemas for runtime validation of query args, responses, and errors. Any [Standard Schema](https://standardschema.dev/) compliant library can be used.

When used with TypeScript, schemas can also be used to [infer the type of that value instead of having to declare it](../usage-with-typescript.mdx#schema-validation).

:::warning

By default, schema failures are treated as _fatal_, meaning that normal error handling such as tag invalidation will not be executed.

In order for schema failures to be treated as non-fatal, you must provide a [`catchSchemaFailure`](#catchschemafailure) function, to convert the schema failure into an error shape matching the base query errors.

```ts title="catchSchemaFailure example" no-transpile
const api = createApi({
  baseQuery: fetchBaseQuery({ baseUrl: '/' }),
  catchSchemaFailure: (error, info) => ({
    status: 'CUSTOM_ERROR',
    error: error.schemaName + ' failed validation',
    data: error,
  }),
  endpoints: (build) => ({
    // ...
  }),
})
```

:::

#### `argSchema`

_(optional)_

[summary](docblock://query/endpointDefinitions.ts?token=CommonEndpointDefinition.argSchema)

[examples](docblock://query/endpointDefinitions.ts?token=CommonEndpointDefinition.argSchema)

#### `responseSchema`

_(optional)_

[summary](docblock://query/endpointDefinitions.ts?token=CommonEndpointDefinition.responseSchema)

[examples](docblock://query/endpointDefinitions.ts?token=CommonEndpointDefinition.responseSchema)

#### `rawResponseSchema`

_(optional, not applicable with `queryFn`)_

[summary](docblock://query/endpointDefinitions.ts?token=EndpointDefinitionWithQuery.rawResponseSchema)

[examples](docblock://query/endpointDefinitions.ts?token=EndpointDefinitionWithQuery.rawResponseSchema)

#### `errorResponseSchema`

_(optional)_

[summary](docblock://query/endpointDefinitions.ts?token=CommonEndpointDefinition.errorResponseSchema)

[examples](docblock://query/endpointDefinitions.ts?token=CommonEndpointDefinition.errorResponseSchema)

#### `rawErrorResponseSchema`

_(optional, not applicable with `queryFn`)_

[summary](docblock://query/endpointDefinitions.ts?token=EndpointDefinitionWithQuery.rawErrorResponseSchema)

Use `rawErrorResponseSchema` to validate the error value returned by your `baseQuery` **before** `transformErrorResponse` runs.

This is the error-side equivalent of `rawResponseSchema`. If you want to validate the final value that your endpoint exposes after `transformErrorResponse`, use [`errorResponseSchema`](#errorresponseschema) instead.

In practice:

- `rawErrorResponseSchema` validates the original error shape returned from the server or `baseQuery`
- `errorResponseSchema` validates the transformed error shape that ends up in RTK Query's cache and hooks

[examples](docblock://query/endpointDefinitions.ts?token=EndpointDefinitionWithQuery.rawErrorResponseSchema)

#### `metaSchema`

_(optional)_

[summary](docblock://query/endpointDefinitions.ts?token=CommonEndpointDefinition.metaSchema)

[examples](docblock://query/endpointDefinitions.ts?token=CommonEndpointDefinition.metaSchema)

## Return value

See [the "created Api" API reference](./created-api/overview)

---

### Rtk Query Api FetchBaseQuery (`docs/rtk-query/api/fetchBaseQuery.mdx`)

---
id: fetchBaseQuery
title: fetchBaseQuery
sidebar_label: fetchBaseQuery
hide_title: true
hide_table_of_contents: false
description: 'RTK Query > API: fetchBaseQuery reference'
---

&nbsp;

# `fetchBaseQuery`

This is a very small wrapper around [`fetch`](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API) that aims to simplify HTTP requests. It is not a full-blown replacement for `axios`, `superagent`, or any other more heavyweight library, but it will cover the vast majority of your HTTP request needs.

`fetchBaseQuery` is a factory function that generates a data fetching method compatible with RTK Query's `baseQuery` configuration option. It takes all standard options from fetch's [`RequestInit`](https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/fetch) interface, as well as `baseUrl`, a `prepareHeaders` function, an optional `fetch` function, a `paramsSerializer` function, and a `timeout`.

## Basic Usage

To use it, import it when you are [creating an API service definition](../../tutorials/rtk-query#create-an-api-service), call it as `fetchBaseQuery(options)`, and pass the result as the `baseQuery` field in `createApi`:

```ts title="src/services/pokemon.ts"
// Or from '@reduxjs/toolkit/query/react'
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query'

export const pokemonApi = createApi({
  // Set the baseUrl for every endpoint below
  baseQuery: fetchBaseQuery({ baseUrl: 'https://pokeapi.co/api/v2/' }),
  endpoints: (build) => ({
    getPokemonByName: build.query({
      // Will make a request like https://pokeapi.co/api/v2/pokemon/bulbasaur
      query: (name: string) => `pokemon/${name}`,
    }),
    updatePokemon: build.mutation({
      query: ({ name, patch }) => ({
        url: `pokemon/${name}`,
        // When performing a mutation, you typically use a method of
        // PATCH/PUT/POST/DELETE for REST endpoints
        method: 'PATCH',
        // fetchBaseQuery automatically adds `content-type: application/json` to
        // the Headers and calls `JSON.stringify(patch)`
        body: patch,
      }),
    }),
  }),
})
```

## Signature

```
/* Detailed source-code truncated for AI context efficiency. */
```

## Parameters

### `baseUrl`

_(required)_

Typically a string like `https://api.your-really-great-app.com/v1/`. If you don't provide a `baseUrl`, it defaults to a relative path from where the request is being made. **You should most likely _always_ specify this**.

### `prepareHeaders`

_(optional)_

Allows you to inject headers on every request. You can specify headers at the endpoint level, but you'll typically want to set common headers like `authorization` here. As a convenience mechanism, the second argument allows you to use `getState` to access your redux store in the event you store information you'll need there such as an auth token. Additionally, it provides access to `arg`, `extra`, `endpoint`, `type`, and `forced` to unlock more granular conditional behaviors.

You can mutate the `headers` argument directly, and returning it is optional.

```ts title="prepareHeaders signature" no-transpile
type prepareHeaders = (
  headers: Headers,
  api: {
    getState: () => unknown
    arg: string | FetchArgs
    extra: unknown
    endpoint: string
    type: 'query' | 'mutation'
    forced: boolean | undefined
  },
) => Headers | void
```

### `paramsSerializer`

_(optional)_

A function that can be used to apply custom transformations to the data passed into [`params`](#setting-the-query-string). If you don't provide this, `params` will be stripped of keys with `undefined` values, and then passed to `new URLSearchParams()`. With some API integrations, you may need to leverage this to use something like the [`query-string`](https://github.com/sindresorhus/query-string) library to support different array types.

### `fetchFn`

_(optional)_

A fetch function that overrides the default on the window. Can be useful in SSR environments where you may need to leverage `isomorphic-fetch` or `cross-fetch`.

### `timeout`

_(optional)_

A number in milliseconds that represents the maximum time a request can take before timing out.

### `isJsonContentType`

_(optional)_

A callback that receives a `Headers` object and determines the `body` field of the `FetchArgs` argument should be stringified via `JSON.stringify()`.

The default implementation inspects the `content-type` header, and will match values like `"application/json"` and `"application/vnd.api+json"`.

### `jsonContentType`

_(optional)_

Used when automatically setting the `content-type` header for a request with a jsonifiable body that does not have an explicit `content-type` header. Defaults to `"application/json"`.

## Common Usage Patterns

### Setting default headers on requests

The most common use case for `prepareHeaders` would be to automatically include `authorization` headers for your API requests.

```ts title="Setting a token from a redux store value
// file: store.ts noEmit
export type RootState = { auth: { token: string } }

// file: baseQuery.ts
import { fetchBaseQuery } from '@reduxjs/toolkit/query'
import type { RootState } from './store'

const baseQuery = fetchBaseQuery({
  baseUrl: '/',
  prepareHeaders: (headers, { getState }) => {
    const token = (getState() as RootState).auth.token

    // If we have a token set in state, let's assume that we should be passing it.
    if (token) {
      headers.set('authorization', `Bearer ${token}`)
    }

    return headers
  },
})
```

## Individual query options

There is more behavior that you can define on a per-request basis. The `query` field may return an object containing any of the default `fetch` options available to the `RequestInit` interface, as well as these additional options:

```ts title="endpoint request options"
interface FetchArgs extends RequestInit {
  url: string
  params?: Record<string, any>
  body?: any
  responseHandler?:
    'json' | 'text' | `content-type` | ((response: Response) => Promise<any>)
  validateStatus?: (response: Response, body: any) => boolean
  timeout?: number
}

const defaultValidateStatus = (response: Response) =>
  response.status >= 200 && response.status <= 299
```

### Setting the body

By default, `fetchBaseQuery` assumes that every request you make will be `json`, so in those cases all you have to do is set the `url` and pass a `body` object when appropriate. For other implementations, you can manually set the `Headers` to specify the content type.

#### json

```ts no-transpile
 // omitted
  endpoints: (build) => ({
    updateUser: build.query({
      query: (user: Record<string, string>) => ({
        url: `users`,
        method: 'PUT',
        body: user // Body is automatically converted to json with the correct headers
      }),
    }),
```

#### text

```ts no-transpile
 // omitted
  endpoints: (build) => ({
    updateUser: build.query({
      query: (user: Record<string, string>) => ({
        url: `users`,
        method: 'PUT',
        headers: {
            'content-type': 'text/plain',
        },
        body: user
      }),
    }),
```

### Setting the query string

`fetchBaseQuery` provides a simple mechanism that converts an `object` to a serialized query string by passing the object to `new URLSearchParams()`. If this doesn't suit your needs, you have two options:

1. Pass the `paramsSerializer` option to `fetchBaseQuery` to apply custom transformations
2. Build your own querystring and set it in the `url`

```ts no-transpile
 // omitted
  endpoints: (build) => ({
    updateUser: build.query({
      query: (user: Record<string, string>) => ({
        url: `users`,
        // Assuming no `paramsSerializer` is specified, the user object is automatically converted
        // and produces a url like /api/users?first_name=test&last_name=example
        params: user
      }),
    }),
```

### Parsing a Response

By default, `fetchBaseQuery` assumes that every `Response` you get will be parsed as `json`. In the event that you don't want that to happen, you can customize the behavior by specifying an alternative response handler like `text`, or take complete control and use a custom function that accepts the raw `Response` object &mdash; allowing you to use any [`Response` method](https://developer.mozilla.org/en-US/docs/Web/API/Response).

The `responseHandler` field can be either:

```ts
type ResponseHandler =
  'content-type' | 'json' | 'text' | ((response: Response) => Promise<any>)
```

The `"json"` and `"text"` values instruct `fetchBaseQuery` to the corresponding fetch response methods for reading the body. `content-type` will check the header field to first determine if this appears to be JSON, and then use one of those two methods. The callback allows you to process the body yourself.

```ts title="Parse a Response as text"
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query'

export const customApi = createApi({
  baseQuery: fetchBaseQuery({ baseUrl: '/api/' }),
  endpoints: (build) => ({
    getUsers: build.query({
      query: () => ({
        url: `users`,
        // This is the same as passing 'text'
        responseHandler: (response) => response.text(),
      }),
    }),
  }),
})
```

:::note Note about responses that return an undefined body
If you make a `json` request to an API that only returns a `200` with an undefined body, `fetchBaseQuery` will pass that through as `undefined` and will not try to parse it as `json`. This can be common with some APIs, especially on `delete` requests.
:::

#### Default response handler

The default response handler is `"json"`, which is equivalent to the following function:

```ts title="Default responseHandler"
const defaultResponseHandler = async (res: Response) => {
  const text = await res.text()
  return text.length ? JSON.parse(text) : null
}
```

### Handling non-standard Response status codes

By default, `fetchBaseQuery` will `reject` any `Response` that does not have a status code of `2xx` and set it to `error`. This is the same behavior you've most likely experienced with `axios` and other popular libraries. In the event that you have a non-standard API you're dealing with, you can use the `validateStatus` option to customize this behavior.

```ts title="Using a custom validateStatus"
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query'

export const customApi = createApi({
  // Set the baseUrl for every endpoint below
  baseQuery: fetchBaseQuery({ baseUrl: '/api/' }),
  endpoints: (build) => ({
    getUsers: build.query({
      query: () => ({
        url: `users`,
        // Example: we have a backend API always returns a 200,
        // but sets an `isError` property when there is an error.
        validateStatus: (response, result) =>
          response.status === 200 && !result.isError,
      }),
    }),
  }),
})
```

### Adding a custom timeout to requests

By default, `fetchBaseQuery` has no default timeout value set, meaning your requests will stay pending until your api resolves the request(s) or it reaches the browser's default timeout (normally 5 minutes). Most of the time, this isn't what you'll want. When using `fetchBaseQuery`, you have the ability to set a `timeout` on the `baseQuery` or on individual endpoints. When specifying both options, the endpoint value will take priority.

```ts title="Setting a timeout value"
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query'

export const api = createApi({
  // Set a default timeout of 10 seconds
  baseQuery: fetchBaseQuery({ baseUrl: '/api/', timeout: 10000 }),
  endpoints: (build) => ({
    getUsers: build.query({
      query: () => ({
        url: `users`,
        // Example: we know the users endpoint is _really fast_ because it's always cached.
        // We can assume if it's over > 1000ms, something is wrong and we should abort the request.
        timeout: 1000,
      }),
    }),
  }),
})
```

---

### Rtk Query Api SetupListeners (`docs/rtk-query/api/setupListeners.mdx`)

---
id: setupListeners
title: setupListeners
sidebar_label: setupListeners
hide_title: true
hide_table_of_contents: false
description: 'RTK Query > API: setupListeners reference'
---

&nbsp;

# `setupListeners`

A utility used to enable `refetchOnFocus` and `refetchOnReconnect` behaviors. It requires the `dispatch` method from your store. Calling `setupListeners(store.dispatch)` will configure listeners with the recommended defaults, but you have the option of providing a callback for more granular control.

```
/* Detailed source-code truncated for AI context efficiency. */
```

If you notice, `onFocus`, `onFocusLost`, `onOffline`, `onOnline` are all actions that are provided to the callback. Additionally, these actions are made available to `api.internalActions` and are able to be used by dispatching them like this:

```ts title="Manual onFocus event" no-transpile
dispatch(api.internalActions.onFocus())
```

---

### Rtk Query Internal BuildSlice (`docs/rtk-query/internal/buildSlice.mdx`)

# BuildSlice

## Slices

### querySlice

#### reducers

- `removeQueryResult` - delete a specific cacheKey's stored result
- `queryResultPatched` - patch a specific cacheKey's result

#### extraReducers - matching queryThunk cases

- `queryThunk.pending`
  - Initially sets QueryStatus to uninitialized
  - updates QueryStatus to pending
  - Generates requestId
  - stores originalArgs
  - stores startedTimeStamp
- `queryThunk.fulfilled`
  - handles merge functionality first
  - otherwise updates the cache data, creates a fulfilledTimeStamp and deletes the substates error

```ts no-transpile
if (merge) {
  if (substate.data !== undefined) {
    const { fulfilledTimeStamp, arg, baseQueryMeta, requestId } = meta
    // There's existing cache data. Let the user merge it in themselves.
    // We're already inside an Immer-powered reducer, and the user could just mutate `substate.data`
    // themselves inside of `merge()`. But, they might also want to return a new value.
    // Try to let Immer figure that part out, save the result, and assign it to `substate.data`.
    let newData = createNextState(substate.data, (draftSubstateData) => {
      // As usual with Immer, you can mutate _or_ return inside here, but not both
      return merge(draftSubstateData, payload, {
        arg: arg.originalArgs,
        baseQueryMeta,
        fulfilledTimeStamp,
        requestId,
      })
    })
    substate.data = newData
  } else {
    // Presumably a fresh request. Just cache the response data.
    substate.data = payload
  }
}
```

- `queryThunk.rejected`
  - utilises `condition()` from `queryThunk` and does nothing if the rejection is a result of `condition()` (indicates a thunk is already running here)
  - else substate.error is set and the status is changed to rejected
- `hasRehydrationInfo`
  - iterates through and resets entries for all fulfilled or rejected status

### mutationSlice

#### reducers

- `removeMutationResult`
  - calls `getMutationCacheKey` from payload
  - if cacheKey is in draft it deletes `draft[cacheKey`(?)

#### extraReducers - matching mutationThunk cases

- `mutationThunk.pending`
  - exits if track is set to false
  - otherwise updates appropriate cacheKey with requestId, pending status and startedTimeStamp
- `mutationThunk.fulfilled`
  - exits if track is set to false
  - otherwise sets data off payload and fulfilledTimeStamp
- `mutationThunk.rejected`
  - exits if track is set to false
  - otherwise sets error and status to rejected
- `hasRehydrationInfo`
  - iterates through and resets entries for all fulfilled or rejected status

### invalidationSlice

#### reducers

- updateProvidedBy
  - takes queryCacheKey and providedTags from payload
  - appends to a list of idSubscriptions the queryCacheKey that are currently subscribed to for each tag

#### extraReducers

- `querySlice.actions.removeQueryResult`,
  - deletes relevant queryCacheKey entry from list of subscription ids
- `hasRehydrationInfo`
  - TODO
- `queryThunk.fulfilled` or `queryThunk.rejected`
  - gets list of tags from action and endpoint definition
  - gets queryCacheKey
  - calls updateProvidedBy action

### subscriptionSlice / internalSubscriptionSlice

#### reducers

- updateSubscriptionOptions
- unsubscribeQueryResult
- internal_getRTKQSubscriptions
- subscriptionsUpdated
  - applyPatches() to the state from the payload

### configSlice

#### reducers

- middlewareRegistered
  - toggles whether the middleware is registered or if there is a conflict

#### extraReducers

- `onOnline`
  - manages state.online in response to listenerMiddleware
- `onOffline`
  - manages state.online in response to listenerMiddleware
- `onFocus`
  - manages state.focused in response to listenerMiddleware
- `onFocusLost`
  - manages state.focused in response to listenerMiddleware
- `hasRehydrationInfo`
  - lists a comment that says: "update the state to be a new object to be picked up as a "state change" by redux-persist's `autoMergeLevel2`"

## Functions

### `updateQuerySubstateIfExists`

Utility function that takes the api/endpoint state, queryCacheKey and Update function.
The "SubState" is determined by accessing the `queryCacheKey` value inside the state. If the substate exists, the update function is executed on the substate.

```js no-transpile
function updateQuerySubstateIfExists(state, queryCacheKey, update) {
  const substate = state[queryCacheKey]
  if (substate) {
    update(substate)
  }
}
```

### `getMutationCacheKey`

conditionally determines the cachekey to be used for the mutation, prioritising the argument provided, followed by the provided cacheKey, and the generated requestId otherwise

```ts no-transpile
export function getMutationCacheKey(
  id:
    | { fixedCacheKey?: string; requestId?: string }
    | MutationSubstateIdentifier
    | { requestId: string; arg: { fixedCacheKey?: string | undefined } },
): string | undefined {
  return ('arg' in id ? id.arg.fixedCacheKey : id.fixedCacheKey) ?? id.requestId
}
```

### `getMutationSubstateIfExists`

same as query version except it uses the id instead of the queryCacheKey, and uses the `getMutationCacheKey` to determine the cachekey

```js no-transpile
function updateMutationSubstateIfExists(state, id, update) {
  const substate = state[getMutationCacheKey(id)]
  if (substate) {
    update(substate)
  }
}
```

---

### Rtk Query Internal QueryThunk (`docs/rtk-query/internal/queryThunk.mdx`)

# queryThunk

## Overview

A core action instantiated during (buildThunks). `queryThunk` leverages `createAsyncThunk` to initiate the query process and is used extensively throughout the codebase both as a match case and to initiate queries. The payload for the query is built using [executeEndpoint]

## Functions

Before executing the payload creator function in [executeEndpoint], `queryThunk` executes two functions:

### `getPendingMeta()`

1. `getPendingMeta()` - adds additional metadata to the action to be used in reducers or middleware.
   1. `startedTimeStamp`
   2. `SHOULD_AUTOBATCH`

### `condition()`

Performs conditional checks based on the provided args to decide whether the query should continue or not. (also attaches the field `dispatchConditionRejected: true` as confirmation that the condition was checked)

```ts no-transpile
if (isUpsertQuery(queryThunkArgs)) { return true }
if (requestState?.status === "pending") { return false }
if (isForcedQuery(queryThunkArgs, state)) { return true }
if (isQueryDefinition(endpointDefinition) && endpointDefinition?.forceRefetch?.({ return true }
if (fulfilledVal) { return false }
else return true
```

## Middleware Uses

### buildSlice

The query endpoint is built almost entirely off of the `extraReducers` matching a `queryThunk` pending/fulfilled/rejected actions and updates the `querySubstate` plus meta data accordingly. The query slice utilises the condition and attached metadata created by a `queryThunk`.

`buildSlice` additionally matches resolved (rejected OR fulfilled) `queryThunks` to update providedTags.

### invalidationByTags

matches against all rejected/fulfilled cases for `queryThunk`

### Polling

matches against multiple queryThunk cases

```js no-transpile
if (
  queryThunk.pending.match(action) ||
  (queryThunk.rejected.match(action) && action.meta.condition)
) {
  updatePollingInterval(action.meta.arg, mwApi)
}
if (
  queryThunk.fulfilled.match(action) ||
  (queryThunk.rejected.match(action) && !action.meta.condition)
) {
  startNextPoll(action.meta.arg, mwApi)
}
```

### cacheLifecycle

uses `queryThunk` matching to differentiate between mutation cache and query cache handling

### queryLifecycle

leverages the createAsyncThunk pending/fulfilled/rejected to extend the lifecycle with query specific traits, also uses it to handle onQueryStarted

### batchedActions

### buildMiddleware

`refetchQuery` refires queryThunk with arguments for the `queryThunk` to determine if the query should be sent or not

---

### Rtk Query Usage Automated Refetching (`docs/rtk-query/usage/automated-refetching.mdx`)

---
id: automated-refetching
title: Automated Re-fetching
sidebar_label: Automated Re-fetching
hide_title: true
description: 'RTK Query > Usage > Automated Refetching: cache invalidation management'
---

&nbsp;

# Automated Re-fetching

As seen under [Default Cache Behavior](./cache-behavior.mdx#default-cache-behavior), when a subscription is added for a query endpoint, a request will be sent only if the cache data does not already exist. If it exists, the existing data will be served instead.

RTK Query uses a "cache tag" system to automate re-fetching for query endpoints that have data affected by mutation endpoints. This enables designing your API such that firing a specific mutation will cause a certain query endpoint to consider its cached data _invalid_, and re-fetch the data if there is an active subscription.

Each endpoint + parameter combination contributes its own `queryCacheKey`. The cache tag system enables the ability to inform RTK Query that a particular query cache has _provided_ specific tags. If a mutation is fired which is said to `invalidate` tags that a query cache has _provided_, the cached data will be considered _invalidated_, and re-fetch if there is an active subscription to the cached data.

For triggering re-fetching through other means, see [Manipulating Cache Behavior](./cache-behavior.mdx#manipulating-cache-behavior).

## Definitions

### Tags

_see also: [tagTypes API reference](../api/createApi.mdx#tagtypes)_

For RTK Query, _tags_ are just a name that you can give to a specific collection of data to control caching and invalidation behavior for re-fetching purposes. It can be considered as a 'label' attached to cached data that is read after a mutation, to decide whether the data should be affected by the mutation.

Tags are defined in the `tagTypes` argument when defining an api. For example, in an application that has both `Posts` and `Users`, you might define `tagTypes: ['Post', 'User']` when calling `createApi`.

An individual `tag` has a `type`, represented as a `string` name, and an optional `id`, represented as a `string` or `number`. It can be represented as a plain string (such as `'Post'`), or an object in the shape `{type: string, id?: string|number}` (such as `[{type: 'Post', id: 1}]`).

### Providing tags

_see also: [providesTags API reference](../api/createApi.mdx#providestags)_

A _query_ can have its cached data _provide_ tags. Doing so determines which 'tag' is attached to the cached data returned by the query.

The `providesTags` argument can either be an array of `string` (such as `['Post']`), `{type: string, id?: string|number}` (such as `[{type: 'Post', id: 1}]`), or a callback that returns such an array. That function will be passed the result as the first argument, the response error as the second argument, and the argument originally passed into the `query` method as the third argument. Note that either the result or error arguments may be undefined based on whether the query was successful or not.

### Invalidating tags

_see also: [invalidatesTags API reference](../api/createApi.mdx#invalidatestags)_

A _mutation_ can _invalidate_ specific cached data based on the tags. Doing so determines which cached data will be either refetched or removed from the cache.

The `invalidatesTags` argument can either be an array of `string` (such as `['Post']`), `{type: string, id?: string|number}` (such as `[{type: 'Post', id: 1}]`), or a callback that returns such an array. That function will be passed the result as the first argument, the response error as the second argument, and the argument originally passed into the `query` method as the third argument. Note that either the result or error arguments may be undefined based on whether the mutation was successful or not.

## Cache tags

RTK Query uses the concept of 'tags' to determine whether a mutation for one endpoint intends to _invalidate_ some data that was _provided_ by a query from another endpoint.

If cache data is being invalidated, it will either refetch the providing query (if components are still using that data) or remove the data from the cache.

When defining an API slice, `createApi` accepts an array of tag type names for the `tagTypes` property, which is a list of possible tag name options that the queries for the API slice could provide.

The example below declares that endpoints can possibly provide 'Posts' and/or 'Users' to the cache:

```ts title="Example of declaring cache tags"
// file: types.ts noEmit
export interface Post {
  id: number
  name: string
}

export interface User {
  id: number
  name: string
}

// file: api.ts
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query'
import type { Post, User } from './types'

const api = createApi({
  baseQuery: fetchBaseQuery({
    baseUrl: '/',
  }),
  // highlight-start
  tagTypes: ['Post', 'User'],
  // highlight-end
  endpoints: (build) => ({
    getPosts: build.query<Post[], void>({
      query: () => '/posts',
    }),
    getUsers: build.query<User[], void>({
      query: () => '/users',
    }),
    addPost: build.mutation<Post, Omit<Post, 'id'>>({
      query: (body) => ({
        url: 'post',
        method: 'POST',
        body,
      }),
    }),
    editPost: build.mutation<Post, Partial<Post> & Pick<Post, 'id'>>({
      query: (body) => ({
        url: `post/${body.id}`,
        method: 'POST',
        body,
      }),
    }),
  }),
})
```

By declaring these tags as what can possibly be provided to the cache, it enables control for individual mutation endpoints to claim whether they affect specific portions of the cache or not, in conjunction with `providesTags` and `invalidatesTags` on individual endpoints.

### Providing cache data

Each individual `query` endpoint can have its cached data _provide_ particular tags. Doing so enables a relationship between cached data from one or more query endpoints and the behavior of one or more mutation endpoints.

The `providesTags` property on a `query` endpoint is used for this purpose.

:::info

Provided tags have no inherent relationship across separate `query` endpoints. Provided tags are used to determine whether cached data returned by an endpoint should be `invalidated` and either be refetched or removed from the cache. If two separate endpoints provide the same tags, they will still contribute their own distinct cached data, which could later both be invalidated by a single tag declared from a mutation.

:::

The example below declares that the `getPosts` `query` endpoint `provides` the `'Post'` tag to the cache, using the `providesTags` property for a `query` endpoint.

```ts title="Example of providing tags to the cache"
// file: types.ts noEmit
export interface Post {
  id: number
  name: string
}

export interface User {
  id: number
  name: string
}

// file: api.ts
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query'
import type { Post, User } from './types'

const api = createApi({
  baseQuery: fetchBaseQuery({
    baseUrl: '/',
  }),
  tagTypes: ['Post', 'User'],
  endpoints: (build) => ({
    getPosts: build.query<Post[], void>({
      query: () => '/posts',
      // highlight-start
      providesTags: ['Post'],
      // highlight-end
    }),
    getUsers: build.query<User[], void>({
      query: () => '/users',
      // highlight-start
      providesTags: ['User'],
      // highlight-end
    }),
    addPost: build.mutation<Post, Omit<Post, 'id'>>({
      query: (body) => ({
        url: 'posts',
        method: 'POST',
        body,
      }),
    }),
    editPost: build.mutation<Post, Partial<Post> & Pick<Post, 'id'>>({
      query: (body) => ({
        url: `post/${body.id}`,
        method: 'POST',
        body,
      }),
    }),
  }),
})
```

For more granular control over the provided data, provided `tags` can have an associated `id`. This enables a distinction between 'any of a particular tag type', and 'a specific instance of a particular tag type'.

The example below declares that the provided posts are associated with particular IDs as determined by the result returned by the endpoint:

```ts title="Example of providing tags with IDs to the cache"
// file: types.ts noEmit
export interface Post {
  id: number
  name: string
}

export interface User {
  id: number
  name: string
}

// file: api.ts
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query'
import type { Post, User } from './types'

const api = createApi({
  baseQuery: fetchBaseQuery({
    baseUrl: '/',
  }),
  tagTypes: ['Post', 'User'],
  endpoints: (build) => ({
    getPosts: build.query<Post[], void>({
      query: () => '/posts',
      // highlight-start
      providesTags: (result, error, arg) =>
        result
          ? [...result.map(({ id }) => ({ type: 'Post' as const, id })), 'Post']
          : ['Post'],
      // highlight-end
    }),
    getUsers: build.query<User[], void>({
      query: () => '/users',
      providesTags: ['User'],
    }),
    addPost: build.mutation<Post, Omit<Post, 'id'>>({
      query: (body) => ({
        url: 'post',
        method: 'POST',
        body,
      }),
    }),
    editPost: build.mutation<Post, Partial<Post> & Pick<Post, 'id'>>({
      query: (body) => ({
        url: `post/${body.id}`,
        method: 'POST',
        body,
      }),
    }),
  }),
})
```

Note that for the example above, the `id` is used where possible on a successful result. In the case of an error, no result is supplied, and we still consider that it has provided the general `'Post'` tag type rather than any specific instance of that tag.

:::tip Advanced List Invalidation
In order to provide stronger control over invalidating the appropriate data, you can use an arbitrary ID such as `'LIST'` for a given tag. See [Advanced Invalidation with abstract tag IDs](#advanced-invalidation-with-abstract-tag-ids) for additional details.
:::

### Invalidating cache data

Each individual mutation endpoint can `invalidate` particular tags for existing cached data. Doing so enables a relationship between cached data from one or more query endpoints and the behavior of one or more mutation endpoints.

The `invalidatesTags` property on a mutation endpoint is used for this purpose.

The example below declares that the `addPost` and `editPost` mutation endpoints `invalidate` any cached data with the `'Post'` tag, using the `invalidatesTags` property for a mutation endpoint:

```ts title="Example of invalidating tags in the cache"
// file: types.ts noEmit
export interface Post {
  id: number
  name: string
}

export interface User {
  id: number
  name: string
}

// file: api.ts
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query'
import type { Post, User } from './types'

const api = createApi({
  baseQuery: fetchBaseQuery({
    baseUrl: '/',
  }),
  tagTypes: ['Post', 'User'],
  endpoints: (build) => ({
    getPosts: build.query<Post[], void>({
      query: () => '/posts',
      providesTags: (result, error, arg) =>
        result
          ? [...result.map(({ id }) => ({ type: 'Post' as const, id })), 'Post']
          : ['Post'],
    }),
    getUsers: build.query<User[], void>({
      query: () => '/users',
      providesTags: ['User'],
    }),
    addPost: build.mutation<Post, Omit<Post, 'id'>>({
      query: (body) => ({
        url: 'post',
        method: 'POST',
        body,
      }),
      // highlight-start
      invalidatesTags: ['Post'],
      // highlight-end
    }),
    editPost: build.mutation<Post, Partial<Post> & Pick<Post, 'id'>>({
      query: (body) => ({
        url: `post/${body.id}`,
        method: 'POST',
        body,
      }),
      // highlight-start
      invalidatesTags: ['Post'],
      // highlight-end
    }),
  }),
})
```

For the example above, this tells RTK Query that after the `addPost` and/or `editPost` mutations are called and completed, any cache data supplied with the `'Post'` tag is no longer valid. If a component is currently subscribed to the cached data for a `'Post'` tag after the above mutations are called and complete, it will automatically re-fetch in order to retrieve up to date data from the server.

An example scenario would be like so:

1. A component is rendered which is using the `useGetPostsQuery()` hook to subscribe to that endpoint's cached data
2. The `/posts` request is fired off, and server responds with posts with IDs 1, 2 & 3
3. The `getPosts` endpoint stores the received data in the cache, and internally registers that the following tags have been provided:
   
   ```js
   [
     { type: 'Post', id: 1 },
     { type: 'Post', id: 2 },
     { type: 'Post', id: 3 },
   ]
   ```
4. The `editPost` mutation is fired off to alter a particular post
5. Upon completion, RTK Query internally registers that the `'Post'` tag is now invalidated, and removes the previously provided `'Post'` tags from the cache
6. Since the `getPosts` endpoint has provided tags of type `'Post'` which now has invalid cache data, and the component is still subscribed to the data, the `/posts` request is automatically fired off again, fetching new data and registering new tags for the updated cached data

For more granular control over the invalidated data, invalidated `tags` can have an associated `id` in the same manner as `providesTags`. This enables a distinction between 'any of a particular tag type' and 'a specific instance of a particular tag type'.

The example below declares that the `editPost` mutation invalidates a specific instance of a `Post` tag, using the ID passed in when calling the mutation function:

```ts title="Example of invalidating tags with IDs to the cache"
// file: types.ts noEmit
export interface Post {
  id: number
  name: string
}

export interface User {
  id: number
  name: string
}

// file: api.ts
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query'
import type { Post, User } from './types'

const api = createApi({
  baseQuery: fetchBaseQuery({
    baseUrl: '/',
  }),
  tagTypes: ['Post', 'User'],
  endpoints: (build) => ({
    getPosts: build.query<Post[], void>({
      query: () => '/posts',
      providesTags: (result, error, arg) =>
        result
          ? [...result.map(({ id }) => ({ type: 'Post' as const, id })), 'Post']
          : ['Post'],
    }),
    getUsers: build.query<User[], void>({
      query: () => '/users',
      providesTags: ['User'],
    }),
    addPost: build.mutation<Post, Omit<Post, 'id'>>({
      query: (body) => ({
        url: 'post',
        method: 'POST',
        body,
      }),
      invalidatesTags: ['Post'],
    }),
    editPost: build.mutation<Post, Partial<Post> & Pick<Post, 'id'>>({
      query: (body) => ({
        url: `post/${body.id}`,
        method: 'POST',
        body,
      }),
      // highlight-start
      invalidatesTags: (result, error, arg) => [{ type: 'Post', id: arg.id }],
      // highlight-end
    }),
  }),
})
```

For the example above, rather than invalidating any tag with the type `'Post'`, calling the `editPost` mutation function will now only invalidate a tag for the provided `id`. I.e. if cached data from an endpoint does not provide a `'Post'` for that same `id`, it will remain considered as 'valid', and will not be triggered to automatically re-fetch.

:::tip Using abstract tag IDs
In order to provide stronger control over invalidating the appropriate data, you can use an arbitrary ID such as `'LIST'` for a given tag. See [Advanced Invalidation with abstract tag IDs](#advanced-invalidation-with-abstract-tag-ids) for additional details.
:::

## Tag Invalidation Behavior

The matrix below shows examples of which invalidated tags will affect and invalidate which provided tags:

<table className="checkbox-table">
  <thead>
    <tr>
      <th className="diagonal-cell">
        <div className="diagonal-cell--content">
          <div className="diagonal-cell--topRight">Provided</div>
          <div className="diagonal-cell--bottomLeft">Invalidated</div>
        </div>
      </th>
      <th>
        <div>General tag A</div>
        <div style={{ fontWeight: 'normal', fontSize: '0.9rem' }}>
          {"['Post']"}
          <br />
          {'/'}
          <br />
          {"[{ type: 'Post' }]"}
        </div>
      </th>
      <th>
        <div>General tag B</div>
        <div style={{ fontWeight: 'normal', fontSize: '0.9rem' }}>
          {"['User']"}
          <br />
          {'/'}
          <br />
          {"[{ type: 'User' }]"}
        </div>
      </th>
      <th>
        <div>Specific tag A1</div>
        <div style={{ fontWeight: 'normal', fontSize: '0.9rem' }}>
          {"[{ type: 'Post',"}
          <br />
          {' id: 1 }]'}
        </div>
      </th>
      <th>
        <div>Specific tag A2</div>
        <div style={{ fontWeight: 'normal', fontSize: '0.9rem' }}>
          {"[{ type: 'Post', id: 'LIST' }]"}
        </div>
      </th>
      <th>
        <div>Specific tag B1</div>
        <div style={{ fontWeight: 'normal', fontSize: '0.9rem' }}>
          {"[{ type: 'User',"}
          <br />
          {' id: 1 }]'}
        </div>
      </th>
      <th>
        <div>Specific tag B2</div>
        <div style={{ fontWeight: 'normal', fontSize: '0.9rem' }}>
          {"[{ type: 'User',"}
          <br />
          {' id: 2 }]'}
        </div>
      </th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>
        <div style={{ fontWeight: 'bold' }}>General tag A</div>
        <div style={{ fontSize: '0.9rem' }}>
          {"['Post'] / [{ type: 'Post' }]"}
        </div>
      </td>
      <td>✔️</td>
      <td></td>
      <td>✔️</td>
      <td>✔️</td>
      <td></td>
      <td></td>
    </tr>
    <tr>
      <td>
        <div style={{ fontWeight: 'bold' }}>General tag B</div>
        <div style={{ fontSize: '0.9rem' }}>
          {"['User'] /"}
          <br />
          {"[{ type: 'User' }]"}
        </div>
      </td>
      <td></td>
      <td>✔️</td>
      <td></td>
      <td></td>
      <td>✔️</td>
      <td>✔️</td>
    </tr>
    <tr>
      <td>
        <div style={{ fontWeight: 'bold' }}>Specific tag A1</div>
        <div style={{ fontSize: '0.9rem' }}>{"[{ type: 'Post', id: 1 }]"}</div>
      </td>
      <td></td>
      <td></td>
      <td>✔️</td>
      <td></td>
      <td></td>
      <td></td>
    </tr>
    <tr>
      <td>
        <div style={{ fontWeight: 'bold' }}>Specific tag A2</div>
        <div style={{ fontSize: '0.9rem' }}>
          {"[{ type: 'Post', id: 'LIST' }]"}
        </div>
      </td>
      <td></td>
      <td></td>
      <td></td>
      <td>✔️</td>
      <td></td>
      <td></td>
    </tr>
    <tr>
      <td>
        <div style={{ fontWeight: 'bold' }}>Specific tag B1</div>
        <div style={{ fontSize: '0.9rem' }}>{"[{ type: 'User', id: 1 }]"}</div>
      </td>
      <td></td>
      <td></td>
      <td></td>
      <td></td>
      <td>✔️</td>
      <td></td>
    </tr>
    <tr>
      <td>
        <div style={{ fontWeight: 'bold' }}>Specific tag B2</div>
        <div style={{ fontSize: '0.9rem' }}>{"[{ type: 'User', id: 2 }]"}</div>
      </td>
      <td></td>
      <td></td>
      <td></td>
      <td></td>
      <td></td>
      <td>✔️</td>
    </tr>
  </tbody>
</table>

The invalidation behavior is summarized based on tag specificity in the sections below.

### General tag

e.g. `['Post'] / [{ type: 'Post' }]`

Will `invalidate` any `provided` tag with the matching type, including general and specific tags.

Example:  
If a general tag of `Post` was invalidated, endpoints whose data `provided` the following tags would all have their data invalidated:

- `['Post']`
- `[{ type: 'Post' }]`
- `[{ type: 'Post' }, { type: 'Post', id: 1 }]`
- `[{ type: 'Post', id: 1 }]`
- `[{ type: 'Post', id: 1 }, { type: 'User' }]`
- `[{ type: 'Post', id: 'LIST' }]`
- `[{ type: 'Post', id: 1 }, { type: 'Post', id: 'LIST' }]`

Endpoints whose data `provided` the following tags would _not_ have their data invalidated:

- `['User']`
- `[{ type: 'User' }]`
- `[{ type: 'User', id: 1 }]`
- `[{ type: 'User', id: 'LIST' }]`
- `[{ type: 'User', id: 1 }, { type: 'User', id: 'LIST' }]`

### Specific tag

e.g. `[{ type: 'Post', id: 1 }]`

Will `invalidate` any `provided` tag with both the matching type, _and_ matching id. Will not cause a `general` tag to be invalidated directly, but _might_ invalidate data for an endpoint that provides a `general` tag _if_ it also provides a matching `specific` tag.

Example 1:
If a specific tag of `{ type: 'Post', id: 1 }` was invalidated, endpoints whose data `provided` the following tags would all have their data invalidated:

- `[{ type: 'Post' }, { type: 'Post', id: 1 }]`
- `[{ type: 'Post', id: 1 }]`
- `[{ type: 'Post', id: 1 }, { type: 'User' }]`
- `[{ type: 'Post', id: 1 }, { type: 'Post', id: 'LIST' }]`

Endpoints whose data `provided` the following tags would _not_ have their data invalidated:

- `['Post']`
- `[{ type: 'Post' }]`
- `[{ type: 'Post', id: 'LIST' }]`
- `['User']`
- `[{ type: 'User' }]`
- `[{ type: 'User', id: 1 }]`
- `[{ type: 'User', id: 'LIST' }]`
- `[{ type: 'User', id: 1 }, { type: 'User', id: 'LIST' }]`

Example 2:
If a specific tag of `{ type: 'Post', id: 'LIST' }` was invalidated, endpoints whose data `provided` the following tags would all have their data invalidated:

- `[{ type: 'Post', id: 'LIST' }]`
- `[{ type: 'Post', id: 1 }, { type: 'Post', id: 'LIST' }]`

Endpoints whose data `provided` the following tags would _not_ have their data invalidated:

- `['Post']`
- `[{ type: 'Post' }]`
- `[{ type: 'Post' }, { type: 'Post', id: 1 }]`
- `[{ type: 'Post', id: 1 }]`
- `[{ type: 'Post', id: 1 }, { type: 'User' }]`
- `['User']`
- `[{ type: 'User' }]`
- `[{ type: 'User', id: 1 }]`
- `[{ type: 'User', id: 'LIST' }]`
- `[{ type: 'User', id: 1 }, { type: 'User', id: 'LIST' }]`

## Recipes

### Advanced Invalidation with abstract tag IDs

While using an 'entity ID' for a tag `id` is a common use case, the `id` property is not intended to be limited to database IDs alone. The `id` is simply a way to label a subset of a particular collection of data for a particular `tag type`.

A powerful use-case is to use an ID like `'LIST'` as a label for data provided by a bulk query, _as well as_ using entity IDs for the individual items. Doing so allows future `mutations` to declare whether they invalidate the data only if it contains a particular item (e.g. `{ type: 'Post', id: 5 }`), or invalidate the data if it is a `'LIST'` (e.g. `{ type: 'Post', id: 'LIST' }`).

:::info 'LIST' Tag and IDs

1. `LIST` is an arbitrary string - technically speaking, you could use anything you want here, such as `ALL` or `*`. The important thing when choosing a custom id is to make sure there is no possibility of it colliding with an id that is returned by a query result. If you have unknown ids in your query results and don't want to risk it, you can go with point 3 below.
2. You can add _many_ tag types for even more control
   - `[{ type: 'Posts', id: 'LIST' }, { type: 'Posts', id: 'SVELTE_POSTS' }, { type: 'Posts', id: 'REACT_POSTS' }]`
3. If the concept of using an `id` like 'LIST' seems strange to you, you can always add another `tagType` and invalidate its root, but we recommend using the `id` approach as shown.

:::

We can compare the scenarios below to see how using a `'LIST'` id can be leveraged to optimize behavior.

#### Invalidating everything of a type

```ts title="API Definition"
// file: types.ts noEmit
export interface Post {
  id: number
  name: string
}

export interface User {
  id: number
  name: string
}

// file: api.ts
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'
import type { Post, User } from './types'

export const api = createApi({
  baseQuery: fetchBaseQuery({ baseUrl: '/' }),
  tagTypes: ['Posts'],
  endpoints: (build) => ({
    getPosts: build.query<Post[], void>({
      query: () => 'posts',
      providesTags: (result) =>
        result ? result.map(({ id }) => ({ type: 'Posts', id })) : ['Posts'],
    }),
    addPost: build.mutation<Post, Partial<Post>>({
      query: (body) => ({
        url: `post`,
        method: 'POST',
        body,
      }),
      invalidatesTags: ['Posts'],
    }),
    getPost: build.query<Post, number>({
      query: (id) => `post/${id}`,
      providesTags: (result, error, id) => [{ type: 'Posts', id }],
    }),
  }),
})

export const { useGetPostsQuery, useGetPostQuery, useAddPostMutation } = api
```

```tsx no-transpile title="App.tsx"
function App() {
  const { data: posts } = useGetPostsQuery()
  const [addPost] = useAddPostMutation()

  return (
    <div>
      <AddPost onAdd={addPost} />
      <PostsList />
      {/* Assume each PostDetail is subscribed via `const {data} = useGetPostQuery(id)` */}
      <PostDetail id={1} />
      <PostDetail id={2} />
      <PostDetail id={3} />
    </div>
  )
}
```

**What to expect**

When `addPost` is triggered, it would cause each `PostDetail` component to go back into a `isFetching` state because `addPost` invalidates the root tag, which causes _every query_ that provides 'Posts' to be re-run. In most cases, this may not be what you want to do. Imagine if you had 100 posts on the screen that all subscribed to a `getPost` query – in this case, you'd create 100 requests and send a ton of unnecessary traffic to your server, which we're trying to avoid in the first place! Even though the user would still see the last good cached result and potentially not notice anything other than their browser hiccuping, you still want to avoid this.

#### Selectively invalidating lists

```ts title="API Definition"
// file: types.ts noEmit
export interface Post {
  id: number
  name: string
}

export interface User {
  id: number
  name: string
}

// file: api.ts
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'
import type { Post, User } from './types'

export const api = createApi({
  baseQuery: fetchBaseQuery({ baseUrl: '/' }),
  tagTypes: ['Posts'],
  endpoints: (build) => ({
    getPosts: build.query<Post[], void>({
      query: () => 'posts',
      providesTags: (result) =>
        result
          ? [
              ...result.map(({ id }) => ({ type: 'Posts' as const, id })),
              { type: 'Posts', id: 'LIST' },
            ]
          : [{ type: 'Posts', id: 'LIST' }],
    }),
    addPost: build.mutation<Post, Partial<Post>>({
      query(body) {
        return {
          url: `post`,
          method: 'POST',
          body,
        }
      },
      invalidatesTags: [{ type: 'Posts', id: 'LIST' }],
    }),
    getPost: build.query<Post, number>({
      query: (id) => `post/${id}`,
      providesTags: (result, error, id) => [{ type: 'Posts', id }],
    }),
  }),
})

export const { useGetPostsQuery, useAddPostMutation, useGetPostQuery } = api
```

```tsx no-transpile title="App.tsx"
function App() {
  const { data: posts } = useGetPostsQuery()
  const [addPost] = useAddPostMutation()

  return (
    <div>
      <AddPost onAdd={addPost} />
      <PostsList />
      {/* Assume each PostDetail is subscribed via `const {data} = useGetPostQuery(id)` */}
      <PostDetail id={1} />
      <PostDetail id={2} />
      <PostDetail id={3} />
    </div>
  )
}
```

**What to expect**

When `addPost` is fired, it will only cause the `PostsList` to go into an `isFetching` state because `addPost` only invalidates the `'LIST'` id, which causes `getPosts` to rerun (because it provides that specific id). So in your network tab, you would only see 1 new request fire for `GET /posts`. As the singular `getPost` queries have not been invalidated, they will not re-run as a result of `addPost`.

:::info

If you intend for the `addPost` mutation to refresh all posts including individual `PostDetail` components while still only making 1 new `GET /posts` request, this can be done by selecting a part of the data using [`selectFromResult`](./queries.mdx#selecting-data-from-a-query-result).

:::

### Providing errors to the cache

The information provided to the cache is not limited to successful data fetches. The concept can be used to inform RTK Query that when a particular failure has been encountered, to `provide` a specific `tag` for that failed cache data. A separate endpoint can then `invalidate` the data for that `tag`, telling RTK Query to re-attempt the previously failed endpoints if a component is still subscribed to the failed data.

The example below demonstrates an example with the following behavior:

- Provides an `UNAUTHORIZED` cache tag if a query fails with an error code of `401 UNAUTHORIZED`
- Provides an `UNKNOWN_ERROR` cache tag if a query fails with a different error
- Enables a 'login' mutation, which when _successful_, will `invalidate` the data with the `UNAUTHORIZED` tag.  
  This will trigger the `postById` endpoint to re-fire if:
  1. The last call for `postById` had encountered an unauthorized error, and
  2. A component is still subscribed to the cached data
- Enables a 'refetchErroredQueries' mutation which when _called_, will `invalidate` the data with the `UNKNOWN_ERROR` tag.  
  This will trigger the `postById` endpoint to re-fire if:
  1. The last call for `postById` had encountered an unknown error, and
  2. A component is still subscribed to the cached data

```ts
// file: types.ts noEmit
export interface Post {
  id: number
  name: string
}
export interface LoginResponse {}

// file: api.ts
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query'
import { Post, LoginResponse } from './types'

const api = createApi({
  baseQuery: fetchBaseQuery({ baseUrl: 'https://example.com' }),
  tagTypes: ['Post', 'UNAUTHORIZED', 'UNKNOWN_ERROR'],
  endpoints: (build) => ({
    postById: build.query<Post, number>({
      query: (id) => `post/${id}`,
      providesTags: (result, error, id) =>
        result
          ? [{ type: 'Post', id }]
          : error?.status === 401
            ? ['UNAUTHORIZED']
            : ['UNKNOWN_ERROR'],
    }),
    login: build.mutation<LoginResponse, void>({
      query: () => '/login',
      // on successful login, will refetch all currently
      // 'UNAUTHORIZED' queries
      invalidatesTags: (result) => (result ? ['UNAUTHORIZED'] : []),
    }),
    refetchErroredQueries: build.mutation<null, void>({
      queryFn: () => ({ data: null }),
      invalidatesTags: ['UNKNOWN_ERROR'],
    }),
  }),
})
```

### Abstracting common provides/invalidates usage

The code written to `provide` & `invalidate` tags for a given API slice will be dependent on multiple factors, including:

- The shape of the data returned by your backend
- Which tags you expect a given query endpoint to provide
- Which tags you expect a given mutation endpoint to invalidate
- The extent that you wish to use the invalidation feature for

When declaring your API slice, you may feel as though you're duplicating your code. For instance, for two separate endpoints that both provide a list of a particular entity, the `providesTags` declaration may only differ in the `tagType` provided.

e.g.

```ts
// file: types.ts noEmit
export interface Post {
  id: number
  name: string
}

export interface User {
  id: number
  name: string
}

// file: api.ts
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query'
import type { Post, User } from './types'

const api = createApi({
  baseQuery: fetchBaseQuery({ baseUrl: 'https://example.com' }),
  tagTypes: ['Post', 'User'],
  endpoints: (build) => ({
    getPosts: build.query<Post[], void>({
      query: () => `posts`,
      // highlight-start
      providesTags: (result) =>
        result
          ? [
              { type: 'Post', id: 'LIST' },
              ...result.map(({ id }) => ({ type: 'Post' as const, id })),
            ]
          : [{ type: 'Post', id: 'LIST' }],
      // highlight-end
    }),
    getUsers: build.query<User[], void>({
      query: () => `users`,
      // highlight-start
      providesTags: (result) =>
        result
          ? [
              { type: 'User', id: 'LIST' },
              ...result.map(({ id }) => ({ type: 'User' as const, id })),
            ]
          : [{ type: 'User', id: 'LIST' }],
      // highlight-end
    }),
  }),
})
```

You may find it beneficial to define helper functions designed for your particular api to reduce this boilerplate across endpoint definitions, e.g.

```ts
// file: types.ts noEmit
export interface Post {
  id: number
  name: string
}

export interface User {
  id: number
  name: string
}

// file: api.ts
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query'
import type { Post, User } from './types'

// highlight-start
function providesList<R extends { id: string | number }[], T extends string>(
  resultsWithIds: R | undefined,
  tagType: T,
) {
  return resultsWithIds
    ? [
        { type: tagType, id: 'LIST' },
        ...resultsWithIds.map(({ id }) => ({ type: tagType, id })),
      ]
    : [{ type: tagType, id: 'LIST' }]
}
// highlight-end

const api = createApi({
  baseQuery: fetchBaseQuery({ baseUrl: 'https://example.com' }),
  tagTypes: ['Post', 'User'],
  endpoints: (build) => ({
    getPosts: build.query({
      query: () => `posts`,
      // highlight-start
      providesTags: (result) => providesList(result, 'Post'),
      // highlight-end
    }),
    getUsers: build.query({
      query: () => `users`,
      // highlight-start
      providesTags: (result) => providesList(result, 'User'),
      // highlight-end
    }),
  }),
})
```

An example of various abstractions for tag providing/invalidating designed for common rest data formats can be seen in the following gist, including typescript support, and factoring both ['LIST' style advanced tag invalidation](#advanced-invalidation-with-abstract-tag-ids) and ['error' style tag invalidation](#providing-errors-to-the-cache): **[RTK Query cache utils](https://gist.github.com/Shrugsy/6b6af02aef1f783df9d636526c1e05fa)**.

---

### Rtk Query Usage Cache Behavior (`docs/rtk-query/usage/cache-behavior.mdx`)

---
id: cache-behavior
title: Cache Behavior
sidebar_label: Cache Behavior
hide_title: true
description: 'RTK Query > Usage > Cache Behavior: defaults, cache lifetimes, and tradeoffs'
---

&nbsp;

# Cache Behavior

A key feature of RTK Query is its management of cached data. When data is fetched from the server, RTK Query will store the data in the Redux store as a 'cache'. When an additional request is performed for the same data, RTK Query will provide the existing cached data rather than sending an additional request to the server.

RTK Query provides a number of concepts and tools to manipulate the cache behavior and adjust it to your needs.

## Default Cache Behavior

With RTK Query, caching is based on:

- API endpoint definitions
- The serialized query parameters used when components subscribe to data from an endpoint
- Active subscription reference counts

When a subscription is started, the parameters used with the endpoint are serialized and stored internally as a `queryCacheKey` for the request. Any future request that produces the same `queryCacheKey` (i.e. called with the same parameters, factoring serialization) will be de-duped against the original, and will share the same data and updates. i.e. two separate components performing the same request will use the same cached data.

When a request is attempted, if the data already exists in the cache, then that data is served and no new request is sent to the server. Otherwise, if the data does not exist in the cache, then a new request is sent, and the returned response is stored in the cache.

Subscriptions are reference-counted. Additional subscriptions that ask for the same endpoint+params increment the reference count. As long as there is an active 'subscription' to the data (e.g. if a component is mounted that calls a `useQuery` hook for the endpoint), then the data will remain in the cache. Once the subscription is removed (e.g. when last component subscribed to the data unmounts), after an amount of time (default 60 seconds), the data will be removed from the cache. The expiration time can be configured with the `keepUnusedDataFor` property for the [API definition as a whole](../api/createApi.mdx#keepunuseddatafor), as well as on a [per-endpoint](../api/createApi.mdx#keepunuseddatafor-1) basis.

### Cache lifetime & subscription example

Imagine an endpoint that expects an `id` as the query param, and 4 components mounted which are requesting data from this same endpoint:

```ts no-transpile
import { useGetUserQuery } from './api.ts'

function ComponentOne() {
  // component subscribes to the data
  const { data } = useGetUserQuery(1)

  return <div>...</div>
}

function ComponentTwo() {
  // component subscribes to the data
  const { data } = useGetUserQuery(2)

  return <div>...</div>
}

function ComponentThree() {
  // component subscribes to the data
  const { data } = useGetUserQuery(3)

  return <div>...</div>
}

function ComponentFour() {
  // component subscribes to the *same* data as ComponentThree,
  // as it has the same query parameters
  const { data } = useGetUserQuery(3)

  return <div>...</div>
}
```

While four components are subscribed to the endpoint, there are only three distinct combinations of endpoint + query parameters. Query parameters `1` and `2` will each have a single subscriber, while query parameter `3` has two subscribers. RTK Query will make three distinct fetches; one for each unique set of query parameters per endpoint.

Data is kept in the cache as long as at least one active subscriber is interested in that endpoint + parameter combination. When the subscriber reference count reaches zero, a timer is set, and if there are no new subscriptions to that data by the time the timer expires, the cached data will be removed. The default expiration is 60 seconds, which can be configured both for the [API definition as a whole](../api/createApi.mdx#keepunuseddatafor), as well as on a [per-endpoint](../api/createApi.mdx#keepunuseddatafor-1) basis.

If 'ComponentThree' is unmounted in the example above, regardless of how much time passes, the data will remain in the cache due to 'ComponentFour' still being subscribed to the same data, and the subscribe reference count will be `1`. However, once 'ComponentFour' unmounts, the subscriber reference count will be `0`. The data will remain in the cache for the remainder of the expiration time. If no new subscription has been created before the timer expires, the cached data will finally be removed.

## Manipulating Cache Behavior

On top of the default behavior, RTK Query provides a number of methods to re-fetch data earlier in scenarios where it should be considered invalid, or is otherwise deemed suitable to be 'refreshed'.

### Reducing subscription time with `keepUnusedDataFor`

As mentioned above under [Default Cache Behavior](#default-cache-behavior) and [Cache lifetime & subscription example](#cache-lifetime--subscription-example), by default, data will remain in the cache for 60 seconds after the subscriber reference count hits zero.

This value can be configured using the `keepUnusedDataFor` option for both the API definition, as well as per-endpoint. Note that the per-endpoint version, if provided, will overrule a setting on the API definition.

Providing a value to `keepUnusedDataFor` as a number in seconds specifies how long the data should be kept in the cache after the subscriber reference count reaches zero.

```ts title="keepUnusedDataFor configuration"
// file: types.ts noEmit
export interface Post {
  id: number
  name: string
}

// file: api.ts
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'
import type { Post } from './types'

export const api = createApi({
  baseQuery: fetchBaseQuery({ baseUrl: '/' }),
  // highlight-start
  // global configuration for the api
  keepUnusedDataFor: 30,
  // highlight-end
  endpoints: (build) => ({
    getPosts: build.query<Post[], number>({
      query: () => `posts`,
      // highlight-start
      // configuration for an individual endpoint, overriding the api setting
      keepUnusedDataFor: 5,
      // highlight-end
    }),
  }),
})
```

### Re-fetching on demand with `refetch`/`initiate`

In order to achieve complete granular control over re-fetching data, you can use the `refetch` function returned as a result property from a [`useQuery`](../api/created-api/hooks.mdx#usequery) or [`useQuerySubscription`](../api/created-api/hooks.mdx#usequerysubscription) hook.

Calling the `refetch` function will force refetch the associated query.

Alternatively, you can dispatch the `initiate` thunk action for an endpoint, passing the option `forceRefetch: true` to the thunk action creator for the same effect.

```tsx no-transpile title="Force refetch example"
import { useDispatch } from 'react-redux'
import { api, useGetPostsQuery } from './api'

const Component = () => {
  const dispatch = useDispatch()
  const { data, refetch } = useGetPostsQuery({ count: 5 })

  function handleRefetchOne() {
    // force re-fetches the data
    refetch()
  }

  function handleRefetchTwo() {
    // has the same effect as `refetch` for the associated query
    dispatch(
      api.endpoints.getPosts.initiate(
        { count: 5 },
        { subscribe: false, forceRefetch: true },
      ),
    )
  }

  return (
    <div>
      <button onClick={handleRefetchOne}>Force re-fetch 1</button>
      <button onClick={handleRefetchTwo}>Force re-fetch 2</button>
    </div>
  )
}
```

### Encouraging re-fetching with `refetchOnMountOrArgChange`

Queries can be encouraged to re-fetch more frequently than usual via the [`refetchOnMountOrArgChange`](../api/createApi.mdx#refetchonmountorargchange) property. This can be passed to the endpoint as a whole, to individual hook calls, or when dispatching the [`initiate`](../api/created-api/endpoints.mdx#initiate) action (the name of the action creator's option is `forceRefetch`).

`refetchOnMountOrArgChange` is used to encourage re-fetching in additional situations where the default behavior would instead serve cached data.

`refetchOnMountOrArgChange` accepts either a boolean value, or a number as time in seconds.

Passing `false` (the default value) for this property will use the default behavior [described above](#default-cache-behavior).

Passing `true` for this property will cause the endpoint to always refetch when a new subscriber to the query is added. If passed to an individual hook call and not the api definition itself, then this applies only to that hook call. I.e., when the component calling the hook mounts, or the argument changes, it will always refetch, regardless of whether cached data for the endpoint + arg combination already exists.

Passing a `number` as a value in seconds will use the following behavior:

- At the time a query subscription is created:
  - if there is an existing query in the cache, it will compare the current time vs the last fulfilled timestamp for that query,
  - It will refetch if the provided amount of time in seconds has elapsed.
- If there is no query, it will fetch the data.
- If there is an existing query, but the amount of time specified since the last query has not elapsed, it will serve the existing cached data.

```ts title="Configuring re-fetching on subscription if data exceeds a given time"
// file: types.ts noEmit
export interface Post {
  id: number
  name: string
}

// file: api.ts
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'
import type { Post } from './types'

export const api = createApi({
  baseQuery: fetchBaseQuery({ baseUrl: '/' }),
  // highlight-start
  // global configuration for the api
  refetchOnMountOrArgChange: 30,
  // highlight-end
  endpoints: (build) => ({
    getPosts: build.query<Post[], number>({
      query: () => `posts`,
    }),
  }),
})
```

```tsx no-transpile title="Forcing refetch on component mount"
import { useGetPostsQuery } from './api'

const Component = () => {
  const { data } = useGetPostsQuery(
    { count: 5 },
    // highlight-start
    // this overrules the api definition setting,
    // forcing the query to always fetch when this component is mounted
    { refetchOnMountOrArgChange: true },
    // highlight-end
  )

  return <div>...</div>
}
```

### Re-fetching on window focus with `refetchOnFocus`

The [`refetchOnFocus`](../api/createApi.mdx#refetchonfocus) option allows you to control whether RTK Query will try to refetch all subscribed queries after the application window regains focus.

If you specify this option alongside `skip: true`, this will not be evaluated until skip is false.

Note that this requires [`setupListeners`](../api/setupListeners.mdx) to have been called.

This option is available on both the api definition with [`createApi`](../api/createApi.mdx), as well as on the [`useQuery`](../api/created-api/hooks.mdx#usequery), [`useQuerySubscription`](../api/created-api/hooks.mdx#usequerysubscription), [`useLazyQuery`](../api/created-api/hooks.mdx#uselazyquery), and [`useLazyQuerySubscription`](../api/created-api/hooks.mdx#uselazyquerysubscription) hooks.

```ts title="src/services/api.ts"
// file: src/services/types.ts noEmit
export interface Post {
  id: number
  name: string
}

// file: src/services/api.ts
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'
import type { Post } from './types'

export const api = createApi({
  baseQuery: fetchBaseQuery({ baseUrl: '/' }),
  // highlight-start
  // global configuration for the api
  refetchOnFocus: true,
  // highlight-end
  endpoints: (build) => ({
    getPosts: build.query<Post[], number>({
      query: () => `posts`,
    }),
  }),
})
```

```ts title="src/store.ts"
// file: src/services/types.ts noEmit
export interface Post {
  id: number
  name: string
}

// file: src/services/api.ts noEmit
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'
import type { Post } from './types'

export const api = createApi({
  baseQuery: fetchBaseQuery({ baseUrl: '/' }),
  // highlight-start
  // global configuration for the api
  refetchOnFocus: true,
  // highlight-end
  endpoints: (build) => ({
    getPosts: build.query<Post[], number>({
      query: () => `posts`,
    }),
  }),
})

// file: src/store.ts
import { configureStore } from '@reduxjs/toolkit'
import { setupListeners } from '@reduxjs/toolkit/query'
import { api } from './services/api'

export const store = configureStore({
  reducer: {
    [api.reducerPath]: api.reducer,
  },
  middleware: (gDM) => gDM().concat(api.middleware),
})

// highlight-start
// enable listener behavior for the store
setupListeners(store.dispatch)
// highlight-end

export type RootState = ReturnType<typeof store.getState>
```

### Re-fetching on network reconnection with `refetchOnReconnect`

The [`refetchOnReconnect`](../api/createApi.mdx#refetchonreconnect) option on [`createApi`](../api/createApi.mdx) allows you to control whether RTK Query will try to refetch all subscribed queries after regaining a network connection.

If you specify this option alongside `skip: true`, this **will not be evaluated** until `skip` is false.

Note that this requires [`setupListeners`](../api/setupListeners.mdx) to have been called.

This option is available on both the api definition with [`createApi`](../api/createApi.mdx), as well as on the [`useQuery`](../api/created-api/hooks.mdx#usequery), [`useQuerySubscription`](../api/created-api/hooks.mdx#usequerysubscription), [`useLazyQuery`](../api/created-api/hooks.mdx#uselazyquery), and [`useLazyQuerySubscription`](../api/created-api/hooks.mdx#uselazyquerysubscription) hooks.

```ts title="src/services/api.ts"
// file: src/services/types.ts noEmit
export interface Post {
  id: number
  name: string
}

// file: src/services/api.ts
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'
import type { Post } from './types'

export const api = createApi({
  baseQuery: fetchBaseQuery({ baseUrl: '/' }),
  // highlight-start
  // global configuration for the api
  refetchOnReconnect: true,
  // highlight-end
  endpoints: (build) => ({
    getPosts: build.query<Post[], number>({
      query: () => `posts`,
    }),
  }),
})
```

```ts title="src/store.ts"
// file: src/services/types.ts noEmit
export interface Post {
  id: number
  name: string
}

// file: src/services/api.ts noEmit
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'
import type { Post } from './types'

export const api = createApi({
  baseQuery: fetchBaseQuery({ baseUrl: '/' }),
  // highlight-start
  // global configuration for the api
  refetchOnReconnect: true,
  // highlight-end
  endpoints: (build) => ({
    getPosts: build.query<Post[], number>({
      query: () => `posts`,
    }),
  }),
})

// file: src/store.ts
import { configureStore } from '@reduxjs/toolkit'
import { setupListeners } from '@reduxjs/toolkit/query'
import { api } from './services/api'

export const store = configureStore({
  reducer: {
    [api.reducerPath]: api.reducer,
  },
  middleware: (gDM) => gDM().concat(api.middleware),
})

// highlight-start
// enable listener behavior for the store
setupListeners(store.dispatch)
// highlight-end

export type RootState = ReturnType<typeof store.getState>
```

### Re-fetching after mutations by invalidating cache tags

RTK Query uses an optional [cache tag](./automated-refetching.mdx#cache-tags) system to automate re-fetching for query endpoints that have data affected by mutation endpoints.

See [Automated Re-fetching](./automated-refetching.mdx) for full details on this concept.

## Tradeoffs

### No Normalized or De-duplicated Cache

RTK Query deliberately **does _not_ implement a cache that would deduplicate identical items across multiple requests**. There are several reasons for this:

- A fully normalized shared-across-queries cache is a _hard_ problem to solve
- We don't have the time, resources, or interest in trying to solve that right now
- In many cases, simply re-fetching data when it's invalidated works well and is easier to understand
- At a minimum, RTKQ can help solve the general use case of "fetch some data", which is a big pain point for a lot of people

As an example, say that we have an API slice with `getTodos` and `getTodo` endpoints, and our components make the following queries:

- `getTodos()`
- `getTodos({filter: 'odd'})`
- `getTodo({id: 1})`

Each of these query results would include a Todo object that looks like `{id: 1}`.

In a fully normalized de-duplicating cache, only a single copy of this Todo object would be stored. However, RTK Query saves each query result independently in the cache. So, this would result in three separate copies of this Todo being cached in the Redux store. However, if all the endpoints are consistently providing the same tags (such as `{type: 'Todo', id: 1}`), then invalidating that tag will force all the matching endpoints to refetch their data for consistency.

The Redux docs have always recommended [keeping data in a normalized lookup table](/usage/structuring-reducers/normalizing-state-shape) to enable easily finding items by ID and updating them in the store, and [RTK's `createEntityAdapter`](../../api/createEntityAdapter.mdx) was designed to help manage normalized state. Those concepts are still valuable and don't go away. However, if you're using RTK Query to manage caching data, there's less need to manipulate the data that way yourself.

There are a couple additional points that can help here:

- The generated query hooks have [a `selectFromResult` option](../api/created-api/hooks.mdx#usequery) that allow components to read individual pieces of data from a query result. As an example, a `<TodoList>` component might call `useTodosQuery()`, and each individual `<TodoListItem>` could use the same query hook but select from the result to get the right todo object.
- You can use the [`transformResponse` endpoint option](../api/createApi.mdx#transformresponse) to modify the fetched data so that it's [stored in a different shape](./customizing-queries.mdx#customizing-query-responses-with-transformresponse), such as using `createEntityAdapter` to normalize the data _for this one response_ before it's inserted into the cache.

### Further information

- [Reddit: discussion of why RTKQ doesn't have a normalized cache, and tradeoffs](https://www.reddit.com/r/reactjs/comments/my9vrq/redux_toolkit_v16_alpha1_rtk_query_apis/gvxi5t7/)

## Examples

### Cache Subscription Lifetime Demo

This example is a live demo of how the subscriber reference count and the value of `keepUnusedDataFor` interact with each other. The `Subscriptions` and `Queries` (including the cached data) are shown in the demo for you to visualize (note that this can also be viewed in the [Redux Devtools Extension](https://github.com/reduxjs/redux-devtools)).

Two components are mounted, each with the same endpoints query (`useGetUsersQuery(2)`). You will be able to observe that when toggling off the components, the subscriber reference count will be reduced. After toggling off both components such that the subscriber reference count reaches zero, you will observe the cached data under the `Queries` section will persist for 5 seconds (the value of `keepUnusedDataFor` provided for the endpoint in this demo). If the subscriber reference count remains at 0 for the full duration, the cached data will then be removed from the store.

<iframe
  src="https://codesandbox.io/embed/rtk-query-cache-subscription-lifetime-example-77tn4?fontsize=12&runonclick=1&hidenavigation=1&theme=dark&module=%2Fsrc%2Fservice%2Findex.ts"
  style={{
    width: '100%',
    height: '800px',
    border: 0,
    borderRadius: '4px',
    overflow: 'hidden',
  }}
  title="rtk-query-cache-subscription-lifetime-example"
  allow="geolocation; microphone; camera; midi; vr; accelerometer; gyroscope; payment; ambient-light-sensor; encrypted-media; usb"
  sandbox="allow-modals allow-forms allow-popups allow-scripts allow-same-origin"
></iframe>

---

### Rtk Query Usage Code Generation (`docs/rtk-query/usage/code-generation.mdx`)

---
id: code-generation
title: Code Generation
sidebar_label: Code Generation
hide_title: true
description: 'RTK Query > Usage > Code Generation: automated creation of API code'
---

&nbsp;

# Code Generation

RTK Query's API and architecture is oriented around declaring API endpoints up front. This lends itself well to automatically generating API slice definitions from external API schema definitions, such as OpenAPI and GraphQL.

We have early previews of code generation capabilities available as separate tools.

## GraphQL

We provide a [Plugin for GraphQL Codegen](https://www.graphql-code-generator.com/docs/plugins/typescript-rtk-query). You can find the documentation to that on the graphql-codegen homepage.

For a full example on how to use it, you can see [this example project](https://github.com/reduxjs/redux-toolkit/tree/master/examples/query/react/graphql-codegen).

## OpenAPI

We provide a package for RTK Query code generation from OpenAPI schemas. It is published as `@rtk-query/codegen-openapi` and you can find the source code at [`packages/rtk-query-codegen-openapi`](https://github.com/reduxjs/redux-toolkit/tree/master/packages/rtk-query-codegen-openapi).

### Usage

Create an empty api using `createApi` like

```ts no-transpile title="src/store/emptyApi.ts"
// Or from '@reduxjs/toolkit/query' if not using the auto-generated hooks
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'

// initialize an empty api service that we'll inject endpoints into later as needed
export const emptySplitApi = createApi({
  baseQuery: fetchBaseQuery({ baseUrl: '/' }),
  endpoints: () => ({}),
})
```

Generate a config file (json, js or ts) with contents like

```ts no-transpile title="openapi-config.ts"
import type { ConfigFile } from '@rtk-query/codegen-openapi'

const config: ConfigFile = {
  schemaFile: 'https://petstore3.swagger.io/api/v3/openapi.json',
  apiFile: './src/store/emptyApi.ts',
  apiImport: 'emptySplitApi',
  outputFile: './src/store/petApi.ts',
  exportName: 'petApi',
  hooks: true,
}

export default config
```

and then call the code generator:

```bash
npx @rtk-query/codegen-openapi openapi-config.ts
```

#### Generating tags

If your OpenAPI specification uses [tags](https://swagger.io/docs/specification/grouping-operations-with-tags/), you can specify the `tag` option to the codegen.
That will result in all generated endpoints having `providesTags`/`invalidatesTags` declarations for the `tags` of their respective operation definition.

Note that this will only result in string tags with no ids, so it might lead to scenarios where too much is invalidated and unnecessary requests are made on mutation.

In that case you have two options:

1. Use [`endpointOverrides`](#overriding-tags) to customize tags for specific endpoints during code generation
2. Use [`enhanceEndpoints`](../api/created-api/code-splitting.mdx) after generation to manually add more specific `providesTags`/`invalidatesTags` with IDs

### Programmatic usage

```ts no-transpile title="src/store/petApi.ts"
import { generateEndpoints } from '@rtk-query/codegen-openapi'

const api = await generateEndpoints({
  apiFile: './fixtures/emptyApi.ts',
  schemaFile: resolve(__dirname, 'fixtures/petstore.json'),
  filterEndpoints: ['getPetById', 'addPet'],
  hooks: true,
})
```

#### With Node.js Child process

```ts no-transpile title="bin/openapi-codegen.ts"
import { exec } from 'node:child_process'

const cliPath = require.resolve('@rtk-query/codegen-openapi/cli')

// you can also use esbuild-runner (esr) or ts-node instead of tsx
exec(`tsx ${cliPath} config.ts`)
```

### Config file options

#### Simple usage

```ts no-transpile
interface SimpleUsage {
  apiFile: string
  schemaFile: string
  apiImport?: string
  exportName?: string
  argSuffix?: string
  operationNameSuffix?: string
  operationIdTransformer?: 'camelCase' | 'none' | ((operationId: string) => string)
  responseSuffix?: string
  hooks?:
    | boolean
    | { queries: boolean; lazyQueries: boolean; mutations: boolean }
  tag?: boolean
  outputFile: string
  filterEndpoints?:
    | string
    | RegExp
    | EndpointMatcherFunction
    | Array<string | RegExp | EndpointMatcherFunction>
  endpointOverrides?: EndpointOverrides[]
  flattenArg?: boolean
}

export type EndpointOverrides = {
  pattern: EndpointMatcher
} & AtLeastOneOf<{
  type: 'mutation' | 'query'
  parameterFilter: ParameterMatcher
  providesTags: string[]
  invalidatesTags: string[]
}>
  useEnumType?: boolean
  enumStyle?: 'union' | 'enum' | 'as-const'
  outputRegexConstants?: boolean
  httpResolverOptions?: SwaggerParser.HTTPResolverOptions
}

export type EndpointMatcherFunction = (
  operationName: string,
  operationDefinition: OperationDefinition,
) => boolean
```

#### Filtering endpoints

If you only want to include a few endpoints, you can use the `filterEndpoints` config option to filter your endpoints.
Note that endpoints are transformed to camelCase by default. For example, `login_user` will become `loginUser`.
`filterEndpoints` is checked against the transformed endpoint name (after applying [`operationIdTransformer`](#customizing-endpoint-name-generation)).

```ts no-transpile title="openapi-config.ts"
const filteredConfig: ConfigFile = {
  // ...
  // should only have endpoints loginUser, placeOrder, getOrderById, deleteOrder
  filterEndpoints: ['loginUser', /Order/],
}
```

#### Customizing endpoint name generation

By default, each operation's `operationId` is converted to camelCase using lodash `camelCase` (via `oazapfts`). This means consecutive uppercase letters are lowercased — for example, `fetchMyJWTPlease` becomes `fetchMyJwtPlease`.

Use the `operationIdTransformer` option to control this behavior:

- **`"camelCase"`** _(default)_ - applies lodash `camelCase`, matching prior behavior
- **`"none"`** - uses the raw `operationId` string verbatim, preserving casing exactly as written in the schema
- **`(operationId: string) => string`** - applies a custom function for full control

```ts no-transpile title="openapi-config.ts"
import type { ConfigFile } from '@rtk-query/codegen-openapi'

const config = {
  // ...
  // Preserve the exact operationId casing from the schema.
  // fetchMyJWTPlease stays fetchMyJWTPlease instead of becoming fetchMyJwtPlease.
  operationIdTransformer: 'none',
} satisfies ConfigFile

export default config
```

```ts no-transpile title="openapi-config.ts"
import type { ConfigFile } from '@rtk-query/codegen-openapi'

const config = {
  // ...
  // Custom transformer - capitalize the first letter only
  operationIdTransformer: (operationId) =>
    operationId.charAt(0).toUpperCase() + operationId.slice(1),
} satisfies ConfigFile

export default config
```

:::note
When `operationIdTransformer` is `"none"` or a custom function, **every operation in the schema must have an `operationId`**. The codegen will throw an error if any operation is missing one.

When using `filterEndpoints` together with `operationIdTransformer`, the filter is matched against the **transformed** name.
:::

#### Endpoint overrides

If an endpoint is generated as a mutation instead of a query or the other way round, you can override that:

```ts no-transpile title="openapi-config.ts"
const withOverride: ConfigFile = {
  // ...
  endpointOverrides: [
    {
      pattern: 'loginUser',
      type: 'mutation',
    },
  ],
}
```

You can also filter the parameters that are included for an endpoint, as long as they aren't a path parameter. This filter is of type `ParameterMatcher`. For example, to only include parameters that begin with "x-" for the 'loginUser' endpoint, see the below example.

```ts no-transpile title="openapi-config.ts"
const withOverride: ConfigFile = {
  // ...
  endpointOverrides: [
    {
      pattern: 'loginUser',
      parameterFilter: /^x-/,
    },
  ],
}
```

For more complex requirements, consider the other possible matchers, such as a `ParameterMatcherFunction`. The below example filters out any parameters that are in the header of the request.

```ts no-transpile title="openapi-config.ts"
const withOverride: ConfigFile = {
  // ...
  endpointOverrides: [
    {
      pattern: /.*/,
      parameterFilter: (_name, parameter) => parameter.in !== 'header',
    },
  ],
}
```

#### Overriding tags

You can override the `providesTags` and `invalidatesTags` generated for any endpoint, regardless of whether the global `tag` option is enabled:

```ts no-transpile title="openapi-config.ts"
const withTagOverrides: ConfigFile = {
  // ...
  tag: true, // or false - overrides work either way
  endpointOverrides: [
    {
      // Override the tags for a specific query
      pattern: 'getPetById',
      providesTags: ['SinglePet', 'PetDetails'],
    },
    {
      // Remove auto-generated tags by providing an empty array
      pattern: 'deletePet',
      invalidatesTags: [],
    },
    {
      // Add both providesTags AND invalidatesTags to any endpoint
      pattern: 'updatePet',
      providesTags: ['LastUpdatedPet'],
      invalidatesTags: ['Pet', 'PetList'],
    },
  ],
}
```

**Key behaviors:**

- Tag overrides take precedence over auto-generated tags from the OpenAPI `tags` field
- You can use an empty array (`[]`) to explicitly remove tags from an endpoint
- Both `providesTags` and `invalidatesTags` can be set on any endpoint type (query or mutation)
- Overrides work regardless of whether the global `tag: true` option is set

This is useful when:

- The OpenAPI tags don't match your caching strategy
- You need more specific cache invalidation than the default tag generation provides
- A mutation should also provide tags (e.g., login returning user data)
- A query should also invalidate tags (e.g., polling that triggers cache updates)

:::note
When using tag overrides with `tag: false`, the overridden tags will be emitted in the generated code, but they won't be automatically added to `addTagTypes`. You may need to manually add your custom tags to the base API's `tagTypes` array.
:::

#### Generating hooks

Setting `hooks: true` will generate `useQuery` and `useMutation` hook exports. If you also want `useLazyQuery` hooks generated or more granular control, you can also pass an object in the shape of: `{ queries: boolean; lazyQueries: boolean; mutations: boolean }`.

#### Controlling enum generation

By default, OpenAPI `enum` definitions are generated as unions of string literals. Use the `enumStyle` option to control this behavior:

- **`"union"`** _(default)_ - a union of string literals: `type Status = 'available' | 'pending'`
- **`"enum"`** - a TypeScript enum: `enum Status { Available = 'available' }`
- **`"as-const"`** - a const object with a companion type, which provides runtime values like an enum while remaining tree-shakeable and compatible with `isolatedModules`:

```ts no-transpile
export const Status = {
  Available: 'available',
  Pending: 'pending',
} as const
export type Status = (typeof Status)[keyof typeof Status]
```

`useEnumType: true` is equivalent to `enumStyle: 'enum'`. If both options are specified, `enumStyle` takes precedence.

#### Generating regex constants for schema patterns

If your OpenAPI schema uses the [`pattern` keyword](https://swagger.io/docs/specification/data-models/data-types/#pattern) to specify regex validation on string properties, you can export these patterns as JavaScript regex constants by setting `outputRegexConstants: true`.

```ts no-transpile title="openapi-config.ts"
const config: ConfigFile = {
  schemaFile: 'https://petstore3.swagger.io/api/v3/openapi.json',
  apiFile: './src/store/emptyApi.ts',
  outputFile: './src/store/petApi.ts',
  outputRegexConstants: true,
}
```

For a schema with pattern-validated properties like:

```yaml
User:
  type: object
  properties:
    email:
      type: string
      pattern: '^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$'
    phone:
      type: string
      pattern: '^\+?[1-9]\d{1,14}$'
```

The codegen will generate:

```ts no-transpile title="Generated output"
export const userEmailPattern =
  /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/
export const userPhonePattern = /^\+?[1-9]\d{1,14}$/
```

These constants can be used for client-side validation to ensure consistency with API expectations.

:::note
Only string-type properties with non-empty `pattern` values will generate constants. The constant name follows the format `{typeName}{propertyName}Pattern` in camelCase.
:::

#### Multiple output files

```ts no-transpile title="openapi-config.ts"
const config: ConfigFile = {
  schemaFile: 'https://petstore3.swagger.io/api/v3/openapi.json',
  apiFile: './src/store/emptyApi.ts',
  outputFiles: {
    './src/store/user.ts': {
      filterEndpoints: [/user/i],
    },
    './src/store/order.ts': {
      filterEndpoints: [/order/i],
    },
    './src/store/pet.ts': {
      filterEndpoints: [/pet/i],
    },
  },
}
```

#### Custom HTTP resolver options

If you need to customize the HTTP request issued to your server, you can use the `httpResolverOptions` option. This object is passed directly to the `SwaggerParser` instance that fetches the OpenAPI schema.

For example, you can pass custom headers or set a custom request timeout.

```ts no-transpile title="openapi-config.ts"
const config: ConfigFile = {
  schemaFile: 'https://petstore3.swagger.io/api/v3/openapi.json',
  apiFile: './src/store/emptyApi.ts',
  outputFile: './src/store/petApi.ts',
  httpResolverOptions: {
    timeout: 30_000,
    headers: {
      Accept: 'application/json',
      Authorization: 'Basic cmVkdXgtdG9vbGtpdDppcy1ncmVhdA==',
    },
  },
}
```

#### Additional Schemas

By default, the only schemas that are exported are those that are referenced in endpoint definitions. Take the following NestJS example:

```ts no-transpile title="user.controller.ts"
class User {
  id: string
  name: string
}

class LogoutMessage {
  reason: string
}

@ApiExtraModels(LogoutMessage)
@Controller('user')
class UserController {
  getUsers(): User[] {
    return this.getUsers()
  }
}
```

In this case, only the `User` schema would be exported even though we've added `LogoutMessage` as an extra model that will appear in the OpenAPI spec. Setting `exportAllSchemas: true` will export all available schemas, whether they're referenced in endpoint definitions or not.

```ts no-transpile title="openapi-config.ts"
const config: ConfigFile = {
  schemaFile: 'https://petstore3.swagger.io/api/v3/openapi.json',
  apiFile: './src/store/emptyApi.ts',
  outputFile: './src/store/petApi.ts',
  exportAllSchemas: true,
}
```

---

### Rtk Query Usage Code Splitting (`docs/rtk-query/usage/code-splitting.mdx`)

---
id: code-splitting
title: Code Splitting
sidebar_label: Code Splitting
hide_title: true
description: 'RTK Query > Usage > Code Splitting: dynamic injection of endpoints'
---

&nbsp;

# Code Splitting

## Overview

By default, an RTK Query API definition normally has all of the endpoint definitions in a single file. However, in larger applications this can result in very large files that may be harder to maintain. It also means that all of the relevant code is being imported right away.

RTK Query allows dynamically injecting endpoint definitions into an existing API service object. This enables splitting up endpoints into multiple files for maintainability, as well as lazy-loading endpoint definitions and associated code to trim down initial bundle sizes. This can be very beneficial for larger applications that may have _many_ endpoints.

## Injecting Endpoints

`api.injectEndpoints` accepts a collection of endpoint definitions (same as `createApi`), as well as an optional `overrideExisting` parameter.

Calling `api.injectEndpoints` will inject the endpoints into the original API service object, modifying it immediately. It returns **the _same_ API service object reference**. If you're using TypeScript, the return value has the TS types for the new endpoints included. (Unfortunately, it cannot modify the types for the original API reference.)

A typical approach would be to have one empty central API slice definition:

```ts title="Basic setup"
// Or from '@reduxjs/toolkit/query' if not using the auto-generated hooks
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'

// initialize an empty api service that we'll inject endpoints into later as needed
export const emptySplitApi = createApi({
  baseQuery: fetchBaseQuery({ baseUrl: '/' }),
  endpoints: () => ({}),
})
```

and then inject the api endpoints in other files and export them from there - that way you will be sure to always import the endpoints in a way that they are definitely injected.

```ts title="Injecting & exporting additional endpoints"
// file: emptySplitApi.ts noEmit
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'

export const emptySplitApi = createApi({
  baseQuery: fetchBaseQuery({ baseUrl: '/' }),
  endpoints: () => ({}),
})

// file: extendedApi.ts
import { emptySplitApi } from './emptySplitApi'

// NOTE: these are the _SAME_ API reference!
const extendedApi = emptySplitApi.injectEndpoints({
  endpoints: (build) => ({
    example: build.query({
      query: () => 'test',
    }),
  }),
  overrideExisting: false,
})

export const { useExampleQuery } = extendedApi
```

:::tip
If you inject an endpoint that already exists and don't explicitly specify `overrideExisting: true`, the endpoint
will not be overridden. In development mode, you will get a warning about this if `overrideExisting` is set to `false`,
and an error will be throw if set to `'throw'`.
:::

## Enhancing Endpoints

Sometimes you may also need to modify an existing API definition, such as adding additional tag types, or providing additional configuration options to a given endpoint.

`api.enhanceEndpoints` returns an updated and enhanced version of the API slice object, containing the combined endpoint definitions.

This is primarily useful for taking an API slice object that was code-generated from an API schema file like OpenAPI, and adding additional specific hand-written configuration for cache invalidation management on top of the generated endpoint definitions.

For example, `enhanceEndpoints` can be used to modify caching behavior by changing the values of `providesTags`, `invalidatesTags`, and `keepUnusedDataFor`:

```ts
// file: api.ts noEmit
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'

export const api = createApi({
  baseQuery: fetchBaseQuery({ baseUrl: '/' }),
  endpoints: (build) => ({
    getUserByUserId: build.query({
      query() {
        return ''
      },
    }),
    patchUserByUserId: build.mutation({
      query() {
        return ''
      },
    }),
    getUsers: build.query({
      query() {
        return ''
      },
    }),
  }),
})

// file: enhanceEndpoints.ts
import { api } from './api'

const enhancedApi = api.enhanceEndpoints({
  addTagTypes: ['User'],
  endpoints: {
    getUserByUserId: {
      providesTags: ['User'],
    },
    patchUserByUserId: {
      invalidatesTags: ['User'],
    },
    // alternatively, define a function which is called with the endpoint definition as an argument
    getUsers(endpoint) {
      endpoint.providesTags = ['User']
      endpoint.keepUnusedDataFor = 120
    },
  },
})
```

---

### Rtk Query Usage Conditional Fetching (`docs/rtk-query/usage/conditional-fetching.mdx`)

---
id: conditional-fetching
title: Conditional Fetching
sidebar_label: Conditional Fetching
hide_title: true
description: 'RTK Query > Usage > Conditional Fetching: skipping fetching if needed'
---

&nbsp;

# Conditional Fetching

## Overview

Query hooks automatically begin fetching data as soon as the component is mounted. But, there are use cases where you may want to delay fetching data until some condition becomes true. RTK Query supports conditional fetching to enable that behavior.

If you want to prevent a query from automatically running, you can use the `skip` parameter in a hook.

[examples](docblock://query/react/buildHooks.ts?token=UseQuerySubscriptionOptions.skip)

[remarks](docblock://query/react/buildHooks.ts?token=UseQuerySubscriptionOptions.skip)

:::tip
TypeScript users may wish to use [`skipToken`](../api/created-api/hooks.mdx#skiptoken) as an alternative to the `skip` option in order to skip running a query, while still keeping types for the endpoint accurate.
:::

---

### Rtk Query Usage Customizing Create Api (`docs/rtk-query/usage/customizing-create-api.mdx`)

---
id: customizing-create-api
title: Customizing createApi
sidebar_label: Customizing createApi
hide_title: true
description: 'RTK Query > Usage > Customizing createApi: adding customized variations'
---

&nbsp;

# Customizing `createApi`

Currently, RTK Query includes two variants of `createApi`:

- `createBaseApi`, which contains only the UI-agnostic Redux logic (the core module)
- `createApi`, which contains both the core and React hooks modules

You can create your own versions of `createApi` by either specifying non-default options for the modules or by adding your own modules.

## Customizing the React-Redux Hooks

If you want the hooks to use different versions of `useSelector`, `useDispatch` and `useStore`, such as if you are using a custom context, you can pass these in at module creation:

```ts
import * as React from 'react'
import {
  createDispatchHook,
  createSelectorHook,
  createStoreHook,
  ReactReduxContextValue,
} from 'react-redux'
import {
  buildCreateApi,
  coreModule,
  reactHooksModule,
} from '@reduxjs/toolkit/query/react'

const MyContext = React.createContext<ReactReduxContextValue | null>(null)
const customCreateApi = buildCreateApi(
  coreModule(),
  reactHooksModule({
    hooks: {
      useDispatch: createDispatchHook(MyContext),
      useSelector: createSelectorHook(MyContext),
      useStore: createStoreHook(MyContext),
    },
  }),
)
```

## Customizing `createSelector` for RTKQ

Both `coreModule` and `reactHooksModule` accept a `createSelector` option which should be a selector creator instance from Reselect or with an equivalent signature.

```ts
import * as React from 'react'
import { createSelectorCreator, lruMemoize } from '@reduxjs/toolkit'
import {
  buildCreateApi,
  coreModule,
  reactHooksModule,
} from '@reduxjs/toolkit/query/react'

const createLruSelector = createSelectorCreator(lruMemoize)

const customCreateApi = buildCreateApi(
  coreModule({ createSelector: createLruSelector }),
  reactHooksModule({ createSelector: createLruSelector }),
)
```

## Creating your own module

If you want to create your own module, you should review [the react-hooks module](https://github.com/reduxjs/redux-toolkit/blob/b74a52935a5840bebca5acdc8e2265e3b6497afa/src/query/react/module.ts) to see what an implementation would look like.

Here is a very stripped down version:

```ts no-transpile
import {
  BaseQueryFn,
  CoreModule,
  EndpointDefinitions,
  Api,
  Module,
  buildCreateApi,
  coreModule,
} from '@reduxjs/toolkit/query'

export const customModuleName = Symbol()
export type CustomModule = typeof customModuleName

declare module '@reduxjs/toolkit/query' {
  export interface ApiModules<
    BaseQuery extends BaseQueryFn,
    Definitions extends EndpointDefinitions,
    ReducerPath extends string,
    TagTypes extends string,
  > {
    [customModuleName]: {
      endpoints: {
        [K in keyof Definitions]: {
          myEndpointProperty: string
        }
      }
    }
  }
}

export const myModule = (): Module<CustomModule> => ({
  name: customModuleName,
  init(api, options, context) {
    // initialize stuff here if you need to

    return {
      injectEndpoint(endpoint, definition) {
        const anyApi = api as any as Api<
          any,
          Record<string, any>,
          string,
          string,
          CustomModule | CoreModule
        >
        anyApi.endpoints[endpoint].myEndpointProperty = 'test'
      },
    }
  },
})

export const myCreateApi = buildCreateApi(coreModule(), myModule())
```

---

### Rtk Query Usage Customizing Queries (`docs/rtk-query/usage/customizing-queries.mdx`)

---
id: customizing-queries
title: Customizing Queries
sidebar_label: Customizing Queries
hide_title: true
description: 'RTK Query > Usage > Customizing Queries: overriding default query behavior'
---

&nbsp;

# Customizing queries

RTK Query is agnostic as to how your requests resolve. You can use any library you like to handle requests, or no library at all. RTK Query provides reasonable defaults expected to cover the majority of use cases, while also allowing room for customization to alter query handling to fit specific needs.

## Customizing queries with `baseQuery`

The default method to handle queries is via the [`baseQuery`](../api/createApi#basequery) option on [`createApi`](../api/createApi), in combination with the [`query`](../api/createApi.mdx#query) option on an endpoint definition.

To process queries, endpoints are defined with a [`query`](../api/createApi.mdx#query) option, which passes its return value to a common [`baseQuery`](../api/createApi#basequery) function used for the API.

By default, RTK Query ships with [`fetchBaseQuery`](../api/fetchBaseQuery), which is a lightweight [`fetch`](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API) wrapper that automatically handles request headers and response parsing in a manner similar to common libraries like `axios`. If `fetchBaseQuery` alone does not meet your needs, you can customize its behavior with a wrapper function, or create your own [`baseQuery`](../api/createApi.mdx#basequery) function from scratch for [`createApi`](../api/createApi) to use.

See also [`baseQuery API Reference`](../api/createApi.mdx#basequery).

### Implementing a custom `baseQuery`

RTK Query expects a `baseQuery` function to be called with three arguments: `args`, `api`, and `extraOptions`. It is expected to return an object with either a `data` or `error` property, or a promise that resolves to return such an object.

:::tip

Base query and query functions must _always_ catch errors themselves, and return it in an object!

```ts no-transpile
function brokenCustomBaseQuery() {
  // ❌ Don't let this throw by itself
  const data = await fetchSomeData()
  return { data }
}

function correctCustomBaseQuery() {
  // ✅ Catch errors and _return_ them so the RTKQ logic can track it
  try {
    const data = await fetchSomeData()
    return { data }
  } catch (error) {
    return { error }
  }
}
```

:::

#### baseQuery function arguments

```ts title="baseQuery example arguments" no-transpile
const customBaseQuery = (
  // highlight-start
  args,
  { signal, dispatch, getState },
  extraOptions,
  // highlight-end
) => {
  // omitted
}
```

#### baseQuery function return value

1.  ```ts title="Expected success result format" no-transpile
    return { data: YourData }
    ```
2.  ```ts title="Expected error result format" no-transpile
    return { error: YourError }
    ```

```ts title="baseQuery example return value" no-transpile
const customBaseQuery = (
  args,
  { signal, dispatch, getState },
  extraOptions,
) => {
  // highlight-start
  if (Math.random() > 0.5) return { error: 'Too high!' }
  return { data: 'All good!' }
  // highlight-end
}
```

:::note
This format is required so that RTK Query can infer the return types for your responses.
:::

At its core, a `baseQuery` function only needs to have the minimum return value to be valid; an object with a `data` or `error` property. It is up to the user to determine how they wish to use the provided arguments, and how requests are handled within the function itself.

#### fetchBaseQuery defaults

For [`fetchBaseQuery`](../api/fetchBaseQuery) specifically, the return type is as follows:

```ts title="Return types of fetchBaseQuery" no-transpile
Promise<
  | {
      data: any
      error?: undefined
      meta?: { request: Request; response: Response }
    }
  | {
      error: {
        status: number
        data: any
      }
      data?: undefined
      meta?: { request: Request; response: Response }
    }
>
```

1.  ```ts title="Expected success result format with fetchBaseQuery" no-transpile
    return { data: YourData }
    ```
2.  ```ts title="Expected error result format with fetchBaseQuery" no-transpile
    return { error: { status: number, data: YourErrorData } }
    ```

## Customizing query responses with `transformResponse`

Individual endpoints on [`createApi`](../api/createApi.mdx) accept a [`transformResponse`](../api/createApi.mdx) property which allows manipulation of the data returned by a query or mutation before it hits the cache.

`transformResponse` is called with the data that a successful `baseQuery` returns for the corresponding endpoint, and the return value of `transformResponse` is used as the cached data associated with that endpoint call.

By default, the payload from the server is returned directly.

```ts
function defaultTransformResponse(
  baseQueryReturnValue: unknown,
  meta: unknown,
  arg: unknown,
) {
  return baseQueryReturnValue
}
```

To change it, provide a function that looks like:

```ts title="Unpack a deeply nested collection" no-transpile
transformResponse: (response, meta, arg) =>
  response.some.deeply.nested.collection
```

`transformResponse` is called with the `meta` property returned from the `baseQuery` as its second
argument, which can be used while determining the transformed response. The value for `meta` is
dependent on the `baseQuery` used.

```ts title="transformResponse meta example" no-transpile
transformResponse: (response: { sideA: Tracks; sideB: Tracks }, meta, arg) => {
  if (meta?.coinFlip === 'heads') {
    return response.sideA
  }
  return response.sideB
}
```

`transformResponse` is called with the `arg` property provided to the endpoint as its third
argument, which can be used while determining the transformed response. The value for `arg` is
dependent on the `endpoint` used, as well as the argument used when calling the query/mutation.

```ts title="transformResponse arg example" no-transpile
transformResponse: (response: Posts, meta, arg) => {
  return {
    originalArg: arg,
    data: response,
  }
}
```

While there is less need to store the response in a [normalized lookup table](/usage/structuring-reducers/normalizing-state-shape) with RTK Query managing caching data, `transformResponse` can be leveraged to do so if desired.

```ts title="Normalize the response data" no-transpile
transformResponse: (response) =>
  response.reduce((acc, curr) => {
    acc[curr.id] = curr
    return acc
  }, {})

/*
  will convert:
  [
    {id: 1, name: 'Harry'},
    {id: 2, name: 'Ron'},
    {id: 3, name: 'Hermione'},
  ]

  to:
  {
    1: { id: 1, name: "Harry" },
    2: { id: 2, name: "Ron" },
    3: { id: 3, name: "Hermione" },
  }
*/
```

[`createEntityAdapter`](../../api/createEntityAdapter.mdx) can also be used with `transformResponse` to normalize data, while also taking advantage of other features provided by `createEntityAdapter`, including providing an `ids` array, using [`sortComparer`](../../api/createEntityAdapter.mdx#sortcomparer) to maintain a consistently sorted list, as well as maintaining strong TypeScript support.

See also [Websocket Chat API with a transformed response shape](./streaming-updates.mdx#websocket-chat-api-with-a-transformed-response-shape) for an example of `transformResponse` normalizing response data in combination with `createEntityAdapter`, while also updating further data using [`streaming updates`](./streaming-updates.mdx).

## Customizing query responses with `transformErrorResponse`

Individual endpoints on [`createApi`](../api/createApi.mdx) accept a [`transformErrorResponse`](../api/createApi.mdx) property which allows manipulation of the error returned by a query or mutation before it hits the cache.

`transformErrorResponse` is called with the error that a failed `baseQuery` returns for the corresponding endpoint, and the return value of `transformErrorResponse` is used as the cached error associated with that endpoint call.

By default, the payload from the server is returned directly.

```ts
function defaultTransformResponse(
  baseQueryReturnValue: unknown,
  meta: unknown,
  arg: unknown,
) {
  return baseQueryReturnValue
}
```

To change it, provide a function that looks like:

```ts title="Unpack a deeply nested error object" no-transpile
transformErrorResponse: (response, meta, arg) =>
  response.data.some.deeply.nested.errorObject
```

`transformErrorResponse` is called with the `meta` property returned from the `baseQuery` as its second
argument, which can be used while determining the transformed response. The value for `meta` is
dependent on the `baseQuery` used.

```ts title="transformErrorResponse meta example" no-transpile
transformErrorResponse: (
  response: { data: { sideA: Tracks; sideB: Tracks } },
  meta,
  arg,
) => {
  if (meta?.coinFlip === 'heads') {
    return response.data.sideA
  }
  return response.data.sideB
}
```

`transformErrorResponse` is called with the `arg` property provided to the endpoint as its third
argument, which can be used while determining the transformed response. The value for `arg` is
dependent on the `endpoint` used, as well as the argument used when calling the query/mutation.

```ts title="transformErrorResponse arg example" no-transpile
transformErrorResponse: (response: Posts, meta, arg) => {
  return {
    originalArg: arg,
    error: response,
  }
}
```

## Customizing queries with `queryFn`

RTK Query comes with `fetchBaseQuery` out of the box, which makes it straightforward to define endpoints that talk to HTTP URLs (such as a typical REST API). We also have integrations with GraphQL as well. However, at its core, RTK Query is really about tracking loading state and cached values for _any_ async request/response sequence, not just HTTP requests.

RTK Query supports defining endpoints that run arbitrary async logic and return a result. Individual endpoints on [`createApi`](../api/createApi.mdx) accept a [`queryFn`](../api/createApi.mdx#queryfn) property, which let you write your own async function with whatever logic you want inside.

This can be useful for scenarios where you want to have particularly different behavior for a single endpoint, or where the query itself is not relevant, including:

- One-off queries that use a different base URL
- One-off queries that use different request handling, such as automatic re-tries
- One-off queries that use different error handling behavior
- Queries that make requests using a third-party library SDK, such as Firebase or Supabase
- Queries that perform async tasks that are not a typical request/response
- Performing multiple requests with a single query ([example](#performing-multiple-requests-with-a-single-query))
- Leveraging invalidation behavior with no relevant query ([example](#using-a-no-op-queryfn))
- Using [Streaming Updates](./streaming-updates) with no relevant initial request ([example](#streaming-data-with-no-initial-request))

See also [`queryFn API Reference`](../api/createApi.mdx#queryfn) for the type signature and available options.

### Implementing a `queryFn`

A `queryFn` can be thought of as an inline `baseQuery`. It will be called with the same arguments as `baseQuery`, as well as the provided `baseQuery` function itself (`arg`, `api`, `extraOptions`, and `baseQuery`). Similarly to `baseQuery`, it is expected to return an object with either a `data` or `error` property, or a promise that resolves to return such an object.

#### Basic `queryFn` Example

```ts title="Basic queryFn example" no-transpile
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query'
import { userAPI, User } from './userAPI'

const api = createApi({
  baseQuery: fetchBaseQuery({ url: '/' }),
  endpoints: (build) => ({
    // normal HTTP endpoint using fetchBaseQuery
    getPosts: build.query<PostsResponse, void>({
      query: () => ({ url: 'posts' }),
    }),
    // highlight-start
    // endpoint with a custom `queryFn` and separate async logic
    getUser: build.query<User, string>({
      queryFn: async (userId: string) => {
        try {
          const user = await userApi.getUserById(userId)
          // Return the result in an object with a `data` field
          return { data: user }
        } catch (error) {
          // Catch any errors and return them as an object with an `error` field
          return { error }
        }
      },
    }),
    // highlight-end
  }),
})
```

#### queryFn function arguments

```ts title="queryFn example arguments" no-transpile
const queryFn = (
  // highlight-start
  args,
  { signal, dispatch, getState },
  extraOptions,
  baseQuery,
  // highlight-end
) => {
  // omitted
}
```

#### queryFn function return value

1.  ```ts title="Expected success result format" no-transpile
    return { data: YourData }
    ```
2.  ```ts title="Expected error result format" no-transpile
    return { error: YourError }
    ```

```ts title="queryFn example return value" no-transpile
const queryFn = (
  args,
  { signal, dispatch, getState },
  extraOptions,
  baseQuery,
) => {
  // highlight-start
  if (Math.random() > 0.5) return { error: 'Too high!' }
  return { data: 'All good!' }
  // highlight-end
}
```

## Examples - `baseQuery`

### Axios baseQuery

This example implements a very basic axios-based `baseQuery` utility.

```ts title="Basic axios baseQuery"
import { createApi } from '@reduxjs/toolkit/query'
import type { BaseQueryFn } from '@reduxjs/toolkit/query'
import axios from 'axios'
import type { AxiosRequestConfig, AxiosError } from 'axios'

// highlight-start
const axiosBaseQuery =
  (
    { baseUrl }: { baseUrl: string } = { baseUrl: '' },
  ): BaseQueryFn<
    {
      url: string
      method?: AxiosRequestConfig['method']
      data?: AxiosRequestConfig['data']
      params?: AxiosRequestConfig['params']
      headers?: AxiosRequestConfig['headers']
    },
    unknown,
    unknown
  > =>
  async ({ url, method, data, params, headers }) => {
    try {
      const result = await axios({
        url: baseUrl + url,
        method,
        data,
        params,
        headers,
      })
      return { data: result.data }
    } catch (axiosError) {
      const err = axiosError as AxiosError
      return {
        error: {
          status: err.response?.status,
          data: err.response?.data || err.message,
        },
      }
    }
  }
// highlight-end

const api = createApi({
  // highlight-start
  baseQuery: axiosBaseQuery({
    baseUrl: 'https://example.com',
  }),
  // highlight-end
  endpoints(build) {
    return {
      query: build.query({ query: () => ({ url: '/query', method: 'get' }) }),
      mutation: build.mutation({
        query: () => ({ url: '/mutation', method: 'post' }),
      }),
    }
  },
})
```

### GraphQL baseQuery

This example implements a very basic GraphQL-based `baseQuery`.

```ts title="Basic GraphQL baseQuery"
import { createApi } from '@reduxjs/toolkit/query'
import { request, gql, ClientError } from 'graphql-request'

// highlight-start
const graphqlBaseQuery =
  ({ baseUrl }: { baseUrl: string }) =>
  async ({ body }: { body: string }) => {
    try {
      const result = await request(baseUrl, body)
      return { data: result }
    } catch (error) {
      if (error instanceof ClientError) {
        return { error: { status: error.response.status, data: error } }
      }
      return { error: { status: 500, data: error } }
    }
  }
// highlight-end

export const api = createApi({
  // highlight-start
  baseQuery: graphqlBaseQuery({
    baseUrl: 'https://graphqlzero.almansi.me/api',
  }),
  // highlight-end
  endpoints: (build) => ({
    getPosts: build.query({
      query: () => ({
        body: gql`
          query {
            posts {
              data {
                id
                title
              }
            }
          }
        `,
      }),
      transformResponse: (response) => response.posts.data,
    }),
    getPost: build.query({
      query: (id) => ({
        body: gql`
        query {
          post(id: ${id}) {
            id
            title
            body
          }
        }
        `,
      }),
      transformResponse: (response) => response.post,
    }),
  }),
})
```

### Automatic re-authorization by extending fetchBaseQuery

This example wraps [`fetchBaseQuery`](../api/fetchBaseQuery) such that when encountering a [`401 Unauthorized`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/401) error, an additional request is sent to attempt to refresh an authorization token, and re-try to initial query after re-authorizing.

```ts title="Simulating axios-like interceptors with a custom base query"
// file: authSlice.ts noEmit
declare function tokenReceived(args?: any): void
declare function loggedOut(): void
export { tokenReceived, loggedOut }
// file: baseQueryWithReauth.ts
import { fetchBaseQuery } from '@reduxjs/toolkit/query'
import type {
  BaseQueryFn,
  FetchArgs,
  FetchBaseQueryError,
} from '@reduxjs/toolkit/query'
import { tokenReceived, loggedOut } from './authSlice'

const baseQuery = fetchBaseQuery({ baseUrl: '/' })
const baseQueryWithReauth: BaseQueryFn<
  string | FetchArgs,
  unknown,
  FetchBaseQueryError
> = async (args, api, extraOptions) => {
  let result = await baseQuery(args, api, extraOptions)
  if (result.error && result.error.status === 401) {
    // try to get a new token
    const refreshResult = await baseQuery('/refreshToken', api, extraOptions)
    if (refreshResult.data) {
      // store the new token
      api.dispatch(tokenReceived(refreshResult.data))
      // retry the initial query
      result = await baseQuery(args, api, extraOptions)
    } else {
      api.dispatch(loggedOut())
    }
  }
  return result
}
```

#### Preventing multiple unauthorized errors

Using [`async-mutex`](https://github.com/DirtyHairy/async-mutex) to prevent multiple calls to '/refreshToken' when multiple calls fail with [`401 Unauthorized`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/401) errors.

```
/* Detailed source-code truncated for AI context efficiency. */
```

### Automatic retries

RTK Query exports a utility called `retry` that you can wrap the `baseQuery` in your API definition with. It defaults to 5 attempts with a basic exponential backoff.

The default behavior would retry at these intervals:

[remarks](docblock://query/retry.ts?token=defaultBackoff)

[examples](docblock://query/retry.ts?token=retry)

In the event that you didn't want to retry on a specific endpoint, you can just set `maxRetries: 0`.

:::info
It is possible for a hook to return `data` and `error` at the same time. By default, RTK Query will keep whatever the last 'good' result was in `data` until it can be updated or garbage collected.
:::

#### Bailing out of error re-tries

The `retry` utility has a `fail` method property attached which can be used to bail out of retries immediately. This can be used for situations where it is known that additional re-tries would be guaranteed to all fail and would be redundant.

```ts title="Bailing out of error re-tries"
import { createApi, fetchBaseQuery, retry } from '@reduxjs/toolkit/query/react'
import type { FetchArgs } from '@reduxjs/toolkit/query'
interface Post {
  id: number
  name: string
}
type PostsResponse = Post[]

// highlight-start
const staggeredBaseQueryWithBailOut = retry(
  async (args: string | FetchArgs, api, extraOptions) => {
    const result = await fetchBaseQuery({ baseUrl: '/api/' })(
      args,
      api,
      extraOptions,
    )

    // bail out of re-tries immediately if unauthorized,
    // because we know successive re-retries would be redundant
    if (result.error?.status === 401) {
      retry.fail(result.error, result.meta)
    }

    return result
  },
  {
    maxRetries: 5,
  },
)
// highlight-end

export const api = createApi({
  // highlight-start
  baseQuery: staggeredBaseQueryWithBailOut,
  // highlight-end
  endpoints: (build) => ({
    getPosts: build.query<PostsResponse, void>({
      query: () => ({ url: 'posts' }),
    }),
    getPost: build.query<Post, string>({
      query: (id) => ({ url: `post/${id}` }),
      extraOptions: { maxRetries: 8 }, // You can override the retry behavior on each endpoint
    }),
  }),
})
export const { useGetPostsQuery, useGetPostQuery } = api
```

### Adding Meta information to queries

A `baseQuery` can also include a `meta` property in its return value. This can be beneficial in cases where you may wish to include additional information associated with the request such as a request ID or timestamp.

In such a scenario, the return value would look like so:

1.  ```ts title="Expected success result format with meta" no-transpile
    return { data: YourData, meta: YourMeta }
    ```
2.  ```ts title="Expected error result format with meta" no-transpile
    return { error: YourError, meta: YourMeta }
    ```



```
/* Detailed source-code truncated for AI context efficiency. */
```

### Constructing a Dynamic Base URL using Redux state

In some cases, you may wish to have a dynamically altered base url determined from a property in your Redux state. A `baseQuery` has access to a [`getState`](../api/createApi.mdx#basequery-function-arguments) method that provides the current store state at the time it is called. This can be used to construct the desired url using a partial url string, and the appropriate data from your store state.

```
/* Detailed source-code truncated for AI context efficiency. */
```

## Examples - `transformResponse`

### Unpacking deeply nested GraphQL data

```ts title="GraphQL transformation example"
// file: graphqlBaseQuery.ts noEmit
import { BaseQueryFn } from '@reduxjs/toolkit/query'
declare const graphqlBaseQuery: (args: { baseUrl: string }) => BaseQueryFn
declare const gql: (literals: TemplateStringsArray) => void
export { graphqlBaseQuery, gql }

// file: graphqlApi.ts
import { createApi } from '@reduxjs/toolkit/query'
import { graphqlBaseQuery, gql } from './graphqlBaseQuery'

interface Post {
  id: number
  title: string
}

export const api = createApi({
  baseQuery: graphqlBaseQuery({
    baseUrl: '/graphql',
  }),
  endpoints: (build) => ({
    getPosts: build.query<Post[], void>({
      query: () => ({
        body: gql`
          query {
            posts {
              data {
                id
                title
              }
            }
          }
        `,
      }),
      // highlight-start
      transformResponse: (response: { posts: { data: Post[] } }) =>
        response.posts.data,
      // highlight-end
    }),
  }),
})
```

### Normalizing data with `createEntityAdapter`

In the example below, `transformResponse` is used in conjunction with [`createEntityAdapter`](../../api/createEntityAdapter.mdx) to normalize the data before storing it in the cache.

For a response such as:


```ts no-transpile
[
  { id: 1, name: 'Harry' },
  { id: 2, name: 'Ron' },
  { id: 3, name: 'Hermione' },
]
```

The normalized cache data will be stored as:

```ts no-transpile
{
  ids: [1, 3, 2],
  entities: {
    1: { id: 1, name: "Harry" },
    2: { id: 2, name: "Ron" },
    3: { id: 3, name: "Hermione" },
  }
}
```

```ts
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'
import { createEntityAdapter } from '@reduxjs/toolkit'
import type { EntityState } from '@reduxjs/toolkit'

export interface Post {
  id: number
  name: string
}

// highlight-start
const postsAdapter = createEntityAdapter<Post>({
  sortComparer: (a, b) => a.name.localeCompare(b.name),
})
// highlight-end

export const api = createApi({
  baseQuery: fetchBaseQuery({ baseUrl: '/' }),
  endpoints: (build) => ({
    getPosts: build.query<EntityState<Post, number>, void>({
      query: () => `posts`,
      // highlight-start
      transformResponse(response: Post[]) {
        return postsAdapter.addMany(postsAdapter.getInitialState(), response)
      },
      // highlight-end
    }),
  }),
})

export const { useGetPostsQuery } = api
```

## Examples - `queryFn`

### Using a Third-Party SDK

Many services like Firebase and Supabase provide their own SDK to make requests. You can use those SDK methods in a `queryFn`:

```ts title="Basic Third-Party SDK" no-transpile
import { createApi, fakeBaseQuery } from '@reduxjs/toolkit/query/react'
import { supabase } from './supabaseApi'

export const supabaseApi = createApi({
  reducerPath: 'supabaseApi',
  baseQuery: fakeBaseQuery(),
  endpoints: (build) => ({
    getBlogs: build.query({
      queryFn: async () => {
        // Supabase conveniently already has `data` and `error` fields
        const { data, error } = await supabase.from('blogs').select()
        if (error) {
          return { error }
        }
        return { data }
      },
    }),
  }),
})
```

You could also try creating a custom base query that uses the SDK, and define endpoints that pass method names or args into that base query.

### Using a no-op queryFn

In certain scenarios, you may wish to have a `query` or `mutation` where sending a request or returning data is not relevant for the situation. Such a scenario would be to leverage the `invalidatesTags` property to force re-fetch specific `tags` that have been provided to the cache.

See also ["providing errors to the cache"](./automated-refetching.mdx#providing-errors-to-the-cache) to see additional detail and an example for such a scenario to 'refetch errored queries'.

```ts title="Using a no-op queryFn"
// file: types.ts noEmit
export interface Post {
  id: number
  name: string
}
export interface User {
  id: number
  name: string
}

// file: api.ts
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query'
import type { Post, User } from './types'

const api = createApi({
  baseQuery: fetchBaseQuery({ baseUrl: '/' }),
  tagTypes: ['Post', 'User'],
  endpoints: (build) => ({
    getPosts: build.query<Post[], void>({
      query: () => 'posts',
      providesTags: ['Post'],
    }),

    getUsers: build.query<User[], void>({
      query: () => 'users',
      providesTags: ['User'],
    }),

    // highlight-start
    refetchPostsAndUsers: build.mutation<null, void>({
      // The query is not relevant here, so a `null` returning `queryFn` is used
      queryFn: () => ({ data: null }),
      // This mutation takes advantage of tag invalidation behavior to trigger
      // any queries that provide the 'Post' or 'User' tags to re-fetch if the queries
      // are currently subscribed to the cached data
      invalidatesTags: ['Post', 'User'],
    }),
    // highlight-end
  }),
})
```

### Streaming data with no initial request

RTK Query provides the ability for an endpoint to send an initial request for data, followed up with recurring [streaming updates](./streaming-updates.mdx) that perform further updates to the cached data as the updates occur. However, the initial request is optional, and you may wish to use streaming updates without any initial request fired off.

In the example below, a `queryFn` is used to populate the cache data with an empty array, with no initial request sent. The array is later populated using streaming updates via the [`onCacheEntryAdded`](../api/createApi.mdx#oncacheentryadded) endpoint option, updating the cached data as it is received.

```ts title="Streaming data with no initial request"
// file: types.ts noEmit
export interface Message {
  id: number
  channel: 'general' | 'redux'
  userName: string
  text: string
}

// file: api.ts
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query'
import type { Message } from './types'

const api = createApi({
  baseQuery: fetchBaseQuery({ baseUrl: '/' }),
  tagTypes: ['Message'],
  endpoints: (build) => ({
    // highlight-start
    streamMessages: build.query<Message[], void>({
      // The query is not relevant here as the data will be provided via streaming updates.
      // A queryFn returning an empty array is used, with contents being populated via
      // streaming updates below as they are received.
      queryFn: () => ({ data: [] }),
      async onCacheEntryAdded(arg, { updateCachedData, cacheEntryRemoved }) {
        const ws = new WebSocket('ws://localhost:8080')
        // populate the array with messages as they are received from the websocket
        ws.addEventListener('message', (event) => {
          updateCachedData((draft) => {
            draft.push(JSON.parse(event.data))
          })
        })
        await cacheEntryRemoved
        ws.close()
      },
    }),
    // highlight-end
  }),
})
```

### Performing multiple requests with a single query

In the example below, a query is written to fetch all posts for a random user. This is done using a first request for a random user, followed by getting all posts for that user. Using `queryFn` allows the two requests to be included within a single query, avoiding having to chain that logic within component code.

```ts title="Performing multiple requests with a single query"
// file: types.ts noEmit
export interface Post {}
export interface User {
  id: number
}

// file: api.ts
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query'
import type { FetchBaseQueryError } from '@reduxjs/toolkit/query'
import type { Post, User } from './types'

const api = createApi({
  baseQuery: fetchBaseQuery({ baseUrl: '/ ' }),
  endpoints: (build) => ({
    getRandomUserPosts: build.query<Post, void>({
      async queryFn(_arg, _queryApi, _extraOptions, fetchWithBQ) {
        // get a random user
        const randomResult = await fetchWithBQ('users/random')
        if (randomResult.error)
          return { error: randomResult.error as FetchBaseQueryError }
        const user = randomResult.data as User
        const result = await fetchWithBQ(`user/${user.id}/posts`)
        return result.data
          ? { data: result.data as Post }
          : { error: result.error as FetchBaseQueryError }
      },
    }),
  }),
})
```

---

--- METRICS ---
- Files Extracted: 58
- Estimated Token Budget: ~131515 tokens
- Canonical Reference: https://codewiki.google/github.com/reduxjs/redux-toolkit
