You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
- What's changed in Redux Toolkit 2.0 and Redux core 5.0, including breaking changes and new features
17
+
- What's changed in Redux Toolkit 2.0, Redux core 5.0, Reselect 5.0, and Redux Thunk 3.0, including breaking changes and new features
18
18
19
19
:::
20
20
21
21
## Introduction
22
22
23
23
Redux Toolkit has been available since 2019, and today it's the standard way to write Redux apps. We've gone 4+ years without any breaking changes. Now, RTK 2.0 gives us a chance to modernize the packaging, clean up deprecated options, and tighten up some edge cases.
24
24
25
-
Redux Toolkit 2.0 is accompanied by major versions of all the other Redux packages: Redux core 5.0, React-Redux 9.0, Redux Thunk 3.0, and Reselect 5.0.
25
+
**Redux Toolkit 2.0 is accompanied by major versions of all the other Redux packages: Redux core 5.0, React-Redux 9.0, Reselect 5.0, and Redux Thunk 3.0**.
26
26
27
-
This page lists known potentially breaking changes in Redux Toolkit 2.0 and Redux core 5.0, as well as new features in Redux Toolkit 2.0. As a reminder, **you should not need to actually install or use the core `redux` package directly** - RTK wraps that, and re-exports all methods and types.
27
+
This page lists known potentially breaking changes in each of those packages, as well as new features in Redux Toolkit 2.0. As a reminder, **you should not need to actually install or use the core `redux` package directly** - RTK wraps that, and re-exports all methods and types.
28
28
29
29
In practice, **most of the "breaking" changes should not have an actual effect on end users, and we expect that many projects can just update the package versions with very few code changes needed**.
30
30
@@ -49,8 +49,8 @@ We've done local testing of the package, but we ask the community to try out thi
49
49
We've updated the build output in several ways:
50
50
51
51
-**Build output is no longer transpiled!** Instead we target modern JS syntax (ES2020)
52
-
- Moved all build artifacts to live under ./dist/, instead of separate top-level folders
53
-
- The lowest Typescript version we test against is now 4.7
52
+
- Moved all build artifacts to live under `./dist/`, instead of separate top-level folders
53
+
- The lowest Typescript version we test against is now **TS 4.7**.
54
54
55
55
#### Dropping UMD builds
56
56
@@ -70,15 +70,17 @@ If you have strong use cases for us continuing to include UMD build artifacts, p
70
70
71
71
We've always specifically told our users that [actions and state _must_ be serializable](https://redux.js.org/style-guide/#do-not-put-non-serializable-values-in-state-or-actions), and that `action.type`_should_ be a string. This is both to ensure that actions are serializable, and to help provide a readable action history in the Redux DevTools.
72
72
73
-
`store.dispatch(action)` now specifically enforces that `action.type`_must_ be a string and will throw an error if not, in the same way it throws an error if the action is not a plain object.
73
+
`store.dispatch(action)` now specifically enforces that **`action.type`_must_ be a string** and will throw an error if not, in the same way it throws an error if the action is not a plain object.
74
74
75
75
In practice, this was already true 99.99% of the time and shouldn't have any effect on users (especially those using Redux Toolkit and `createSlice`), but there may be some legacy Redux codebases that opted to use Symbols as action types.
76
76
77
77
#### `createStore` Deprecation
78
78
79
79
In [Redux 4.2.0, we marked the original `createStore` method as `@deprecated`](https://github.com/reduxjs/redux/releases/tag/v4.2.0). Strictly speaking, **this is _not_ a breaking change**, nor is it new in 5.0, but we're documenting it here for completeness.
80
80
81
-
**This deprecation is solely a _visual_ indicator that is meant to encourage users to [migrate their apps from legacy Redux patterns to use the modern Redux Toolkit APIs](https://redux.js.org/usage/migrating-to-modern-redux)**. The deprecation results in a visual strikethrough when imported and used, like ~~`createStore`~~, but with _no_ runtime errors or warnings.
81
+
**This deprecation is solely a _visual_ indicator that is meant to encourage users to [migrate their apps from legacy Redux patterns to use the modern Redux Toolkit APIs](https://redux.js.org/usage/migrating-to-modern-redux)**.
82
+
83
+
The deprecation results in a **visual strikethrough** when imported and used, like **~~`createStore`~~**, but with **_no_ runtime errors or warnings**.
82
84
83
85
**`createStore` will continue to work indefinitely, and will _not_ ever be removed**. But, today we want _all_ Redux users to be using Redux Toolkit for all of their Redux logic.
84
86
@@ -94,7 +96,7 @@ To fix this, there are three options:
94
96
95
97
In 2019, we began a community-powered conversion of the Redux codebase to TypeScript. The original effort was discussed in [#3500: Port to TypeScript](https://github.com/reduxjs/redux/issues/3500), and the work was integrated in PR [#3536: Convert to TypeScript](https://github.com/reduxjs/redux/issues/3536).
96
98
97
-
However, the TS-converted code in master has sat around since then, unused and unpublished, due to concerns about possible compatibility issues with the existing ecosystem (as well as general inertia on our part).
99
+
However, the TS-converted code sat around in the repo for several years, unused and unpublished, due to concerns about possible compatibility issues with the existing ecosystem (as well as general inertia on our part).
98
100
99
101
Redux core v5 is now built from that TS-converted source code. In theory, this should be almost identical in both runtime behavior and types to the 4.x build, but it's very likely that some of the changes may cause types issues.
100
102
@@ -339,6 +341,10 @@ Redux 4.1.0 optimized its bundle size by [extracting error message strings out o
339
341
340
342
<divclass="typescript-only">
341
343
344
+
#### `configureStore` field order for `middleware` matters
345
+
346
+
If you are passing _both_ the `middleware` and `enhancers` fields to `configureStore`, the `middleware` field _must_ come first in order for internal TS inference to work properly.
347
+
342
348
#### Non-default middleware/enhancers must use `Tuple`
343
349
344
350
We've seen many cases where users passing the `middleware` parameter to configureStore have tried spreading the array returned by `getDefaultMiddleware()`, or passed an alternate plain array. This unfortunately loses the exact TS types from the individual middleware, and often causes TS problems down the road (such as `dispatch` being typed as `Dispatch<AnyAction>` and not knowing about thunks).
@@ -362,14 +368,86 @@ This same restriction applies to the `enhancers` field.
362
368
363
369
#### Entity adapter type updates
364
370
365
-
`createEntityAdapter` now has an `Id` generic argument, which will be used to strongly type the item IDs anywhere those are exposed. Previously, the ID field type was always `string | number`. TS will now try to infer the exact type from either the `.id` field of your entity type, or the `selectId` return type. You could also fall back to passing that generic type directly.
371
+
`createEntityAdapter` now has an `Id` generic argument, which will be used to strongly type the item IDs anywhere those are exposed. Previously, the ID field type was always `string | number`. TS will now try to infer the exact type from either the `.id` field of your entity type, or the `selectId` return type. You could also fall back to passing that generic type directly.**If you use the `EntityState<Data, Id>` type directly, you _must_ supply both generic arguments!**
366
372
367
373
The `.entities` lookup table is now defined to use a standard TS `Record<Id, MyEntityType>`, which assumes that each item lookup exists by default. Previously, it used a `Dictionary<MyEntityType>` type, which assumed the result was `MyEntityType | undefined`. The `Dictionary` type has been removed.
368
374
369
375
If you prefer to assume that the lookups _might_ be undefined, use TypeScript's `noUncheckedIndexedAccess` configuration option to control that.
370
376
371
377
</div>
372
378
379
+
### Reselect
380
+
381
+
#### `createSelector` Uses `weakMapMemoize` As Default Memoizer
382
+
383
+
**`createSelector` now uses a new default memoization function called `weakMapMemoize`**. This memoizer offers an effectively infinite cache size, which should simplify usage with varying arguments, but relies exclusively on reference comparisons.
384
+
385
+
If you need to customize equality comparisons, customize `createSelector` to use the original `lruMemoize` method instead:
Since the original `defaultMemoize` function is no longer actually the default, we've renamed it to `lruMemoize` for clarity. This only matters if you specifically imported it into your app to customize selectors.
397
+
398
+
#### `createSelector` Dev-Mode Checks
399
+
400
+
`createSelector` now does checks in development mode for common mistakes, like input selectors that always return new references, or result functions that immediately return their argument. These checks can be customized at selector creation or globally.
401
+
402
+
This is important, as an input selector returning a materially different result with the same parameters means that the output selector will never memoize correctly and be run unnecessarily, thus (potentially) creating a new result and causing rerenders.
403
+
404
+
```ts
405
+
const addNumbers =createSelector(
406
+
// this input selector will always return a new reference when run
407
+
// so cache will never be used
408
+
(a, b) => ({ a, b }),
409
+
({ a, b }) => ({ total: a+b })
410
+
)
411
+
// instead, you should have an input selector for each stable piece of data
412
+
const addNumbersStable =createSelector(
413
+
(a, b) =>a,
414
+
(a, b) =>b,
415
+
(a, b) => ({
416
+
total: a+b
417
+
})
418
+
)
419
+
```
420
+
421
+
This is done the first time the selector is called, unless configured otherwise. More details are available in the [Reselect docs on dev-mode checks](https://reselect.js.org/api/development-only-stability-checks).
422
+
423
+
Note that while RTK re-exports `createSelector`, it intentionally does not re-export the function to configure this check globally - if you wish to do so, you should instead depend on `reselect` directly and import it yourself.
424
+
425
+
<divclass="typescript-only">
426
+
427
+
#### `ParametricSelector` Types Removed
428
+
429
+
The `ParametricSelector` and `OutputParametricSelector` types have been removed. Use `Selector` and `OutputSelector` instead.
430
+
431
+
</div>
432
+
433
+
### React-Redux
434
+
435
+
#### Requires React 18
436
+
437
+
React-Redux v7 and v8 worked with all versions of React that supported hooks (16.8+, 17, and 18). v8 switched from internal subscription management to React's new `useSyncExternalStore` hook, but used the "shim" implementation to provide support for React 16.8 and 17, which did not have that hook built in.
438
+
439
+
**React-Redux v9 switches to _requiring_ React 18, and does _not_ support React 16 or 17**. This allows us to drop the shim and save a small bit of bundle size.
440
+
441
+
### Redux Thunk
442
+
443
+
#### Thunk Uses Named Exports
444
+
445
+
The `redux-thunk` package previously used a single default export that was the middleware, with an attached field named `withExtraArgument` that allowed customization.
446
+
447
+
The default export has been removed. There are now two named exports: `thunk` (the basic middleware) and `withExtraArgument`.
448
+
449
+
If you are using Redux Toolkit, this should have no effect, as RTK already handles this inside of `configureStore`.
450
+
373
451
## New Features
374
452
375
453
These features are new in Redux Toolkit 2.0, and help cover additional use cases that we've seen users ask for in the ecosystem.
The existing `createSlice` API now has support for defining [`selectors`](https://redux-toolkit.js.org/api/createSlice#selectors) directly as part of the slice. By default, these will be generated with the assumption that the slice is mounted in the root state using `slice.name` as the field, such as `name: "todos"` -> `rootState.todos`. You can call `sliceObject.getSelectors(selectSliceState)` to generate the selectors with an alternate location, similar to how `entityAdapter.getSelectors()` works.
526
+
The existing `createSlice` API now has support for defining [`selectors`](https://redux-toolkit.js.org/api/createSlice#selectors) directly as part of the slice. By default, these will be generated with the assumption that the slice is mounted in the root state using `slice.name` as the field, such as `name: "todos"` -> `rootState.todos`. Additionally, there's now a `slice.selectSlice` method that does that default root state lookup.
527
+
528
+
You can call `sliceObject.getSelectors(selectSliceState)` to generate the selectors with an alternate location, similar to how `entityAdapter.getSelectors()` works.
449
529
450
530
```ts
451
531
const slice =createSlice({
@@ -516,7 +596,7 @@ We've _wanted_ to include a way to define thunks directly inside of `createSlice
516
596
517
597
We've settled on these compromises:
518
598
519
-
-**In order to create async thunks with `createSlice`, you specifically need to [set up a custom version of `createSlice` that has access to `createAsyncThunk`]https://redux-toolkit.js.org/api/createSlice#createasyncthunk)**.
599
+
-**In order to create async thunks with `createSlice`, you specifically need to [set up a custom version of `createSlice` that has access to `createAsyncThunk`](https://redux-toolkit.js.org/api/createSlice#createasyncthunk)**.
520
600
- You can declare thunks inside of `createSlice.reducers`, by using a "creator callback" syntax for the `reducers` field that is similar to the `build` callback syntax in RTK Query's `createApi` (using typed functions to create fields in an object). Doing this does look a bit different than the existing "object" syntax for the `reducers` field, but is still fairly similar.
521
601
- You can customize _some_ of the types for thunks inside of `createSlice`, but you _cannot_ customize the `state` or `dispatch` types. If those are needed, you can manually do an `as` cast, like `getState() as RootState`.
522
602
@@ -625,33 +705,6 @@ We've updated `configureStore` to add the `autoBatchEnhancer` to the store setup
625
705
626
706
[`entityAdapter.getSelectors()`](https://redux-toolkit.js.org/api/createEntityAdapter#selector-functions) now accepts an options object as its second argument. This allows you to pass in your own preferred `createSelector` method, which will be used to memoize the generated selectors. This could be useful if you want to use one of Reselect's new alternate memoizers, or some other memoization library with an equivalent signature.
627
707
628
-
### New dev checks in Reselect v5
629
-
630
-
Reselect v5 includes a dev-only check to check stability of input selectors, by running them an extra time with the same parameters, and checking that the result matches.
631
-
632
-
This is important, as an input selector returning a materially different result with the same parameters means that the output selector will be run unnecessarily, thus (potentially) creating a new result and causing rerenders.
633
-
634
-
```ts
635
-
const addNumbers =createSelector(
636
-
// this input selector will always return a new reference when run
637
-
// so cache will never be used
638
-
(a, b) => ({ a, b }),
639
-
({ a, b }) => ({ total: a+b })
640
-
)
641
-
// instead, you should have an input selector for each stable piece of data
642
-
const addNumbersStable =createSelector(
643
-
(a, b) =>a,
644
-
(a, b) =>b,
645
-
(a, b) => ({
646
-
total: a+b
647
-
})
648
-
)
649
-
```
650
-
651
-
This is done the first time the selector is called, unless configured otherwise. More details are available in the [README](https://github.com/reduxjs/reselect#inputstabilitycheck).
652
-
653
-
Note that RTK intentionally does not re-export the function to configure this check globally - if you wish to do so, you should instead depend on `reselect` directly and import it yourself.
654
-
655
708
### Immer 10.0
656
709
657
710
[Immer 10.0](https://github.com/immerjs/immer/releases/tag/v10.0.0) is now final, and has several major improvements and updates:
@@ -664,6 +717,12 @@ Note that RTK intentionally does not re-export the function to configure this ch
664
717
665
718
We've updated RTK to depend on the final Immer 10.0 release.
666
719
720
+
### Next.js Setup Guide
721
+
722
+
We now have a docs page that covers [how to set up Redux properly with Next.js](https://redux.js.org/usage/nextjs). We've seen a lot of questions around using Redux, Next, and the App Router together, and this guide should help provide advice.
723
+
724
+
(At this time, the Next.js `with-redux` example is still showing outdated patterns - we're going to file a PR shortly to update that to match our docs guide.)
725
+
667
726
## Recommendations
668
727
669
728
Based on changes in 2.0 and previous versions, there have been some shifts in thinking that are good to know about, if non-essential.
0 commit comments