Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .prettierignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
**/dist/**
**/etc/**
**/temp/**
**/temp/**
**/__testfixtures__/**
20 changes: 20 additions & 0 deletions .yarn/patches/msw-npm-0.40.2-2107d48752
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
diff --git a/lib/types/context/set.d.ts b/lib/types/context/set.d.ts
index 266229bad706ec49392b8b87e18560c1566b490d..4fad485f8ffec2db92e808a05ccd9274414a9bd9 100644
--- a/lib/types/context/set.d.ts
+++ b/lib/types/context/set.d.ts
@@ -15,4 +15,5 @@ export declare type ForbiddenHeaderError<HeaderName extends string> = `SafeRespo
* })
* @see {@link https://mswjs.io/docs/api/context/set `ctx.set()`}
*/
+// @ts-ignore
export declare function set<N extends string | HeadersObject>(...args: N extends string ? Lowercase<N> extends ForbiddenHeaderNames ? ForbiddenHeaderError<N> : [N, string] : N extends HeadersObject<infer CookieName> ? Lowercase<CookieName> extends ForbiddenHeaderNames ? ForbiddenHeaderError<CookieName> : [N] : [N]): ResponseTransformer;
diff --git a/lib/types/sharedOptions.d.ts b/lib/types/sharedOptions.d.ts
index d1d6e05df2dc2c29f06d8d0b91c500a10e651a29..3d8c29fd2089b2abf21d78cd277aac9271e781c2 100644
--- a/lib/types/sharedOptions.d.ts
+++ b/lib/types/sharedOptions.d.ts
@@ -21,4 +21,5 @@ export interface LifeCycleEventsMap<ResponseType> {
'response:bypass': (response: ResponseType, requestId: string) => void;
unhandledException: (error: Error, request: MockedRequest) => void;
}
+// @ts-ignore
export declare type LifeCycleEventEmitter<ResponseType> = Pick<StrictEventEmitter<ResponseType>, 'on' | 'removeListener' | 'removeAllListeners'>;
768 changes: 0 additions & 768 deletions .yarn/releases/yarn-3.1.0.cjs

This file was deleted.

801 changes: 801 additions & 0 deletions .yarn/releases/yarn-3.2.4.cjs

Large diffs are not rendered by default.

6 changes: 3 additions & 3 deletions .yarnrc.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ nodeLinker: node-modules

plugins:
- path: .yarn/plugins/@yarnpkg/plugin-workspace-tools.cjs
spec: "@yarnpkg/plugin-workspace-tools"
spec: '@yarnpkg/plugin-workspace-tools'
- path: .yarn/plugins/@yarnpkg/plugin-version.cjs
spec: "@yarnpkg/plugin-version"
spec: '@yarnpkg/plugin-version'

yarnPath: .yarn/releases/yarn-3.1.0.cjs
yarnPath: .yarn/releases/yarn-3.2.4.cjs
2 changes: 1 addition & 1 deletion CNAME
Original file line number Diff line number Diff line change
@@ -1 +1 @@
cn.redux-toolkit.js.org
redux-toolkit.js.org
143 changes: 143 additions & 0 deletions docs/api/autoBatchEnhancer.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
---
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 } 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

const store = configureStore({
reducer: counterSlice.reducer,
// highlight-start
enhancers: (existingEnhancers) => {
// Add the autobatch enhancer to the store setup
return existingEnhancers.concat(autoBatchEnhancer())
},
// highlight-end
})
```

## 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
```

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. If you add the `autoBatchEnhancer` to the store setup, it will improve the overall UI performance, especially when rendering large lists of components that use the RTKQ query hooks.
77 changes: 77 additions & 0 deletions docs/api/codemods.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
---
id: codemods
title: Codemods
sidebar_label: Codemods
hide_title: true
---

&nbsp;

# Codemods

Per [the description in `1.9.0-alpha.0`](https://github.com/reduxjs/redux-toolkit/releases/tag/v1.9.0-alpha.0), we plan to remove the "object" argument from `createReducer` and `createSlice.extraReducers` in the future RTK 2.0 major version. In `1.9.0-alpha.0`, we added a one-shot runtime warning to each of those APIs.

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 two codemods: `createReducerBuilder` and `createSliceBuilder`.

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)
},
})
```
4 changes: 4 additions & 0 deletions docs/api/createSlice.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,10 @@ The generated `reducer` function is suitable for passing to the Redux `combineRe
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.

> **Note**: the result object is conceptually similar to a
> ["Redux duck" code structure](https://redux.js.org/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 there are a couple caveats to keep in mind:
Expand Down
4 changes: 2 additions & 2 deletions docs/api/immutabilityMiddleware.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,11 @@ interface ImmutableStateInvariantMiddlewareOptions {
*/
isImmutable?: IsImmutableFunc
/**
An array of dot-separated path strings that match named nodes from
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[]
ignoredPaths?: (string | RegExp)[]
/** Print a warning if checks take longer than N ms. Default: 32ms */
warnAfter?: number
// @deprecated. Use ignoredPaths
Expand Down
8 changes: 4 additions & 4 deletions docs/api/otherExports.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -58,10 +58,6 @@ The default immutable update function from the [`immer` library](https://immerjs

[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.

### `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.

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

Expand All @@ -80,6 +76,10 @@ const todosReducer = createReducer(initialState, (builder) => {
})
```

### `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.
Expand Down
13 changes: 7 additions & 6 deletions docs/api/serializabilityMiddleware.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -39,16 +39,17 @@ interface SerializableStateInvariantMiddlewareOptions {
ignoredActions?: string[]

/**
* An array of dot-separated path strings to ignore when checking
* for serializability, Defaults to ['meta.arg', 'meta.baseQueryMeta']
* An array of dot-separated path strings or regular expressions to ignore
* when checking for serializability, Defaults to
* ['meta.arg', 'meta.baseQueryMeta']
*/
ignoredActionPaths?: string[]
ignoredActionPaths?: (string | RegExp)[]

/**
* An array of dot-separated path strings to ignore when checking
* for serializability, Defaults to []
* An array of dot-separated path strings or regular expressions to ignore
* when checking for serializability, Defaults to []
*/
ignoredPaths?: string[]
ignoredPaths?: (string | RegExp)[]
/**
* Execution time warning threshold. If the middleware takes longer
* than `warnAfter` ms, a warning will be displayed in the console.
Expand Down
Loading