Skip to content

Commit 0dab270

Browse files
authored
Merge pull request #4629 from reduxjs/docs/5.0-docs-updates
2 parents e124f0b + e222442 commit 0dab270

File tree

2 files changed

+102
-43
lines changed

2 files changed

+102
-43
lines changed

docs/usage/migrations/migrating-rtk-2.md

Lines changed: 101 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
---
22
id: migrating-rtk-2
3-
title: Migrating to RTK 2.x and Redux 5.x
4-
sidebar_label: Migrating to RTK 2.x and Redux 5.x
3+
title: Migrating to RTK 2.0 and Redux 5.0
4+
sidebar_label: Migrating to RTK 2.0 and Redux 5.0
55
hide_title: true
66
toc_max_heading_level: 4
77
---
@@ -10,21 +10,21 @@ toc_max_heading_level: 4
1010

1111
<div className="migration-guide">
1212

13-
# Migrating to RTK 2.x and Redux 5.x
13+
# Migrating to RTK 2.0 and Redux 5.0
1414

1515
:::tip What You'll Learn
1616

17-
- 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
1818

1919
:::
2020

2121
## Introduction
2222

2323
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.
2424

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**.
2626

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

2929
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**.
3030

@@ -49,8 +49,8 @@ We've done local testing of the package, but we ask the community to try out thi
4949
We've updated the build output in several ways:
5050

5151
- **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**.
5454

5555
#### Dropping UMD builds
5656

@@ -70,15 +70,17 @@ If you have strong use cases for us continuing to include UMD build artifacts, p
7070

7171
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.
7272

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

7575
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.
7676

7777
#### `createStore` Deprecation
7878

7979
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.
8080

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**.
8284

8385
**`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.
8486

@@ -94,7 +96,7 @@ To fix this, there are three options:
9496

9597
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).
9698

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).
98100

99101
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.
100102

@@ -339,6 +341,10 @@ Redux 4.1.0 optimized its bundle size by [extracting error message strings out o
339341

340342
<div class="typescript-only">
341343

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+
342348
#### Non-default middleware/enhancers must use `Tuple`
343349

344350
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).
@@ -352,7 +358,7 @@ import { configureStore, Tuple } from '@reduxjs/toolkit'
352358

353359
configureStore({
354360
reducer: rootReducer,
355-
middleware: getDefaultMidldeware => new Tuple(additionalMiddleware, logger)
361+
middleware: getDefaultMiddleware => new Tuple(additionalMiddleware, logger)
356362
})
357363
```
358364

@@ -362,14 +368,86 @@ This same restriction applies to the `enhancers` field.
362368

363369
#### Entity adapter type updates
364370

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!**
366372

367373
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.
368374

369375
If you prefer to assume that the lookups _might_ be undefined, use TypeScript's `noUncheckedIndexedAccess` configuration option to control that.
370376

371377
</div>
372378

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:
386+
387+
```ts no-emit
388+
createSelector(inputs, resultFn, {
389+
memoize: lruMemoize,
390+
memoizeOptions: { equalityCheck: yourEqualityFunction }
391+
})
392+
```
393+
394+
#### `defaultMemoize` Renamed to `lruMemoize`
395+
396+
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+
<div class="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+
373451
## New Features
374452

375453
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.
@@ -445,7 +523,9 @@ expect(combinedReducer(undefined, dummyAction()).number).toBe(
445523

446524
### `selectors` field in `createSlice`
447525

448-
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.
449529

450530
```ts
451531
const slice = createSlice({
@@ -516,7 +596,7 @@ We've _wanted_ to include a way to define thunks directly inside of `createSlice
516596

517597
We've settled on these compromises:
518598

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)**.
520600
- 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.
521601
- 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`.
522602

@@ -625,33 +705,6 @@ We've updated `configureStore` to add the `autoBatchEnhancer` to the store setup
625705

626706
[`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.
627707

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-
655708
### Immer 10.0
656709

657710
[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
664717

665718
We've updated RTK to depend on the final Immer 10.0 release.
666719

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+
667726
## Recommendations
668727

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

src/types/reducers.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { Action, UnknownAction } from './actions'
33
/* reducers */
44

55
/**
6-
* A *reducer* (also called a *reducing function*) is a function that accepts
6+
* A *reducer* is a function that accepts
77
* an accumulation and a value and returns a new accumulation. They are used
88
* to reduce a collection of values down to a single value
99
*

0 commit comments

Comments
 (0)