Skip to content

Releases: reduxjs/redux-toolkit

v2.2.3

31 Mar 20:42
Compare
Choose a tag to compare

This minor release fixes the types for functions that accept a React Context instance to match the changes in React Redux v9.

What's Changed

  • Update React Redux dependency to v9, and update docs to use .withTypes by @aryaemami59 in #4308

Full Changelog: v2.2.2...v2.2.3

v2.2.2

21 Mar 00:22
Compare
Choose a tag to compare

This patch release fixes an incorrect build setting for the legacy-esm artifacts, and fixes an issue with RTKQ query hooks didn't always remove the cache entries if arguments were changed rapidly.

Changes

legacy-esm Artifact Transpilation

The legacy-esm build artifacts are intended for use by Webpack 4. Those were supposed to be transpiled to target "es2017", but were in fact still set to target "esnext" - an oversight during the 2.0 development cycle. This release fixes that setting, so those artifacts are now correctly transpiled.

Other Fixes

RTKQ query hooks now handle additional actions around argument changes that should result in cache entries being removed.

Additionally, 2.2.1 contained a fix to an incorrectly named type: TypedUseMutationTrigger is now TypedMutationTrigger.

What's Changed

  • rename TypedUseMutationTrigger to TypedMutationTrigger, and add deprecated alias by @EskiMojo14 in #4204
  • Fixed memory leak in rapid hook arg changing by @riqts in #4268
  • Fix incorrect legacy-esm target by @markerikson in #4284

Full Changelog: v2.2.0...v2.2.2

v2.2.0

12 Feb 21:45
Compare
Choose a tag to compare

This minor release:

  • Adds a second parameter to entityAdapter.getInitialState(additionalProps, entities) to allow prefilling state
    • Equivalent to entityAdapter.setAll(entityAdapter.getInitialState(additionalProps), entities)
    • First parameter can be undefined if no additional properties are desired
  • Allows initialising combineSlices with no static reducers
    • Previously const combinedReducer = combineSlices().withLazyLoadedSlices<LazyLoadedSlices>() would have thrown an error
    • Now returns a "no-op" reducer that just returns an empty object until first reducer injected
  • Allows a new 'throw' value for overrideExisting in injectEndpoints, which throws an error if a definition is injected with a name which is already used
  • Exports more type helpers for RTKQ hook and trigger types
  • Exports types related to overriding result types in enhanceEndpoints
  • Fixes state inference for injected slices when undeclared (i.e. not in LazyLoadedSlices)
  • Adds a action.meta.arg.isPrefetch value to query thunk actions when prefetched

What's Changed

New Contributors

Full Changelog: v2.1.0...v2.2.0

v2.1.0

24 Jan 10:28
Compare
Choose a tag to compare

This minor release:

  • adds withTypes methods to listenerMiddleware and createDraftSafeSelector
  • adds a skipPollingIfUnfocused option to RTK Query
  • adds the ability to customise the createSelector instance used by RTK Query
  • reworks slice selector logic to avoid depending on this value
  • fixes the order and inference of create.asyncThunk type parameters
  • fixes requirements for meta fields returned from queryFns
  • marks promises that will never reject as safe, in preparation for typescript-eslint/typescript-eslint#7008

What's Changed

New Contributors

Full Changelog: v2.0.1...v2.1.0

v2.0.0

04 Dec 14:10
Compare
Choose a tag to compare

This major release :

  • Removes the deprecated object syntax from createSlice and createReducer
  • Removes other deprecated options
  • Updates the middleware and enhancers options of configureStore to require callbacks
  • Updates the packaging for better ESM/CJS compatibility and modernizes the build output
  • Includes all changes to Redux core 5.0, Reselect 5.0, and Redux Thunk 3.0
  • Updates RTKQ default subscription behavior
  • Adds a new combineSlices method with support for lazy-loading slice reducers
  • Adds a new "dynamic middleware" middleware with support for adding middleware at runtime
  • Adds a new callback syntax to createSlice.reducers, with optional support for defining thunks inside of createSlice
  • Adds the autoBatchEnhancer to configureStore by default
  • Has many additional TS tweaks and improvements

This release has breaking changes. (Note: v2.0.1 was released with a couple hotfixes for Reselect and Redux Thunk right as this was being finalized.)

This release is part of a wave of major versions of all the Redux packages: Redux Toolkit 2.0, Redux core 5.0, React-Redux 9.0, Reselect 5.0, and Redux Thunk 3.0.

For full details on all of the breaking changes and other significant changes to all of those packages, see the "Migrating to RTK 2.0 and Redux 5.0" migration guide in the Redux docs.

Note

The Redux core, Reselect, and Redux Thunk packages are included as part of Redux Toolkit, and RTK users do not need to manually upgrade them - you'll get them as part of the upgrade to RTK 2.0. (If you're not using Redux Toolkit yet, please start migrating your existing legacy Redux code to use Redux Toolkit today!)

# RTK
npm install @reduxjs/toolkit
yarn add @reduxjs/toolkit

Changelog

Object syntax for createSlice.extraReducers and createReducer removed

RTK's createReducer API was originally designed to accept a lookup table of action type strings to case reducers, like { "ADD_TODO": (state, action) => {} }. We later added the "builder callback" form to allow more flexibility in adding "matchers" and a default handler, and did the same for createSlice.extraReducers.

We have removed the "object" form for both createReducer and createSlice.extraReducers in RTK 2.0, as the builder callback form is effectively the same number of lines of code, and works much better with TypeScript.

As an example, this:

const todoAdded = createAction('todos/todoAdded')

createReducer(initialState, {
  [todoAdded]: (state, action) => {},
})

createSlice({
  name,
  initialState,
  reducers: {
    /* case reducers here */
  },
  extraReducers: {
    [todoAdded]: (state, action) => {},
  },
})

should be migrated to:

createReducer(initialState, (builder) => {
  builder.addCase(todoAdded, (state, action) => {})
})

createSlice({
  name,
  initialState,
  reducers: {
    /* case reducers here */
  },
  extraReducers: (builder) => {
    builder.addCase(todoAdded, (state, action) => {})
  },
})
Codemods

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. More details are available here.

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

Examples:

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 feedback from more real-world codebases!

configureStore Options Changes

configureStore.middleware must be a callback

Since the beginning, configureStore has accepted a direct array value as the middleware option. However, providing an array directly prevents configureStore from calling getDefaultMiddleware(). So, middleware: [myMiddleware] means there is no thunk middleware added (or any of the dev-mode checks).

This is a footgun, and we've had numerous users accidentally do this and cause their apps to fail because the default middleware never got configured.

As a result, we've now made the middleware only accept the callback form. If for some reason you still want to replace all of the built-in middleware, do so by returning an array from the callback:

const store = configureStore({
  reducer,
  middleware: (getDefaultMiddleware) => {
    // WARNING: this means that _none_ of the default middleware are added!
    return [myMiddleware]
    // or for TS users, use:
    // return new Tuple(myMiddleware)
  },
})

But note that we consistently recommend not replacing the default middleware entirely, and that you should use return getDefaultMiddleware().concat(myMiddleware).

configureStore.enhancers must be a callback

Similarly to configureStore.middleware, the enhancers field must also be a callback, for the same reasons.

The callback will receive a getDefaultEnhancers function that can be used to customise the batching enhancer that's now included by default.

For example:

const store = configureStore({
  reducer,
  enhancers: (getDefaultEnhancers) => {
    return getDefaultEnhancers({
      autoBatch: { type: 'tick' },
    }).concat(myEnhancer)
  },
})

It's important to note that the result of getDefaultEnhancers will also contain the middleware enhancer created with any configured/default middleware. To help prevent mistakes, configureStore will log an error to console if middleware was provided and the middleware enhancer wasn't included in the callback result.

const store = configureStore({
  reducer,
  enhancers: (getDefaultEnhancers) => {
    return [myEnhancer] // we've lost the  middleware here
    // instead:
    return getDefaultEnhancers().concat(myEnhancer)
  },
})

Also, note that if you supply the enhancers field, it must come after the middleware field in order for TS inference to work properly.

Standalone getDefaultMiddleware and getType removed

The standalone version of getDefaultMiddleware has been deprecated since v1.6.1, and has now been removed. Use the function passed to the middleware callback instead, which has the correct types.

We have also removed the getType export, which was used to extract a type string from action creators made with createAction. Instead, use the static property actionCreator.type.

RTK Query behaviour changes

We've had a number of reports where RTK Query had issues around usage of dispatch(endpoint.initiate(arg, {subscription: false})). There were also reports that multiple triggered lazy queries were resolving the promises at the wrong time. Both of these had the same underlying issue, which was that RTKQ wasn't tracking cache entries in these cases (intentionally). We've reworked the logic to always track cache entries (and remove them as needed), which should resolve those behavior issues.

We also have had issues raised about trying to run multiple mutations in a row and how tag invalidation behaves. RTKQ now has internal logic to delay tag invalidation briefly, to allow multiple invalidations to get handled together. This is controlled by a new invalidationBehavior: 'immediate' | 'delayed' flag on createApi. The new default behavior is 'delayed'. Set it to 'immediate' to revert to the behavior in RTK 1.9.

In RTK 1.9, we reworked RTK Query's internals to keep most of the subscription status inside the RTKQ middleware. The values are still synced to the Redux store state, but this is primarily for display by the Redux DevTools "RTK Query" panel. Related to the cache entry changes above, we've optimized how often those values get synced to the Redux state for perf.

ESM/CJS Package Compatibility

The biggest theme of the Redux v5 and RTK 2.0 releases is trying to get "true" ESM package publishing compatibility in place, while still supporting CJS in the published package.

The primary build artifact is now an ESM file, dist/redux-toolkit.modern.mjs. Most build tools should pick this up. There's also a CJS artifact, and a second copy of the ESM file named redux-toolkit.legacy-esm.js to support Webpack 4 (which does not recognize the exports field in package.json). Additionally, all of the build artifacts now live under ./dist/ in the published package.

Modernized Build Output

We now publish modern JS syntax targeting ES2020, including optional chaining, object spread, and other modern syntax. If you need to target older browsers, please transpile the packages yourself (or use the legacy-esm build artifact for ES2017).

Build Tooling

We're now building the package using https://github.com/egoist/tsup. We also now include sourcemaps for the ESM and CJS artifacts.

Dropping UMD Builds

Redux has always shipped with UMD build artifacts. These are primarily meant for direct import as script tags, such as in a CodePen or a no-bundler build environment.

We've dropped those build artifacts from the published package, on the grounds that the use cases seem pretty rare today.

There's now a `redux-toolkit.browser.mj...

Read more

v2.0.0-rc.3

02 Dec 04:20
Compare
Choose a tag to compare
v2.0.0-rc.3 Pre-release
Pre-release

This release candidate updates to the latest Reselect 5.0 RC to pick up the rename of defaultMemoize to lruMemoize.

Note that we hope to release Redux Toolkit 2.0, Redux core 5.0, and React-Redux 9.0 by this weekend!

See the preview Redux Toolkit 2.0 + Redux core 5.0 Migration Guide for an overview of breaking changes in RTK 2.0 and Redux core.

The 2.0 integration branch contains the docs preview for the 2.0 changes. Not all changes are documented yet, but you can see API reference pages for most of the new features here:

npm install @reduxjs/toolkit@next

yarn add @reduxjs/toolkit@next

Changelog

RTK Query Selector Usage Fixes

The previous v2.0.0-rc.2 release picked up Reselect's dev mode checks for result functions that just return their input, like x => x. Turns out that some of RTK Query's internal selector usage did that in some cases :) That resulted in a ton of warnings being printed. We've updated our internals to fix that.

Reselect Changes

The Reselect defaultMemoize function has now been renamed to lruMemoize, since it's no longer the default. See the Reselect v5.0.0-rc.1 release notes for more details.

What's Changed

Full Changelog: v2.0.0-rc.2...v2.0.0-rc.3

v2.0.0-rc.2

01 Dec 05:02
Compare
Choose a tag to compare
v2.0.0-rc.2 Pre-release
Pre-release

This release candidate updates to the latest Reselect 5.0 RC to pick up the change to use weakMapMemoize as the default inside createSelector.

Note that we hope to release Redux Toolkit 2.0, Redux core 5.0, and React-Redux 9.0 by the start of December! (If we don't hit that, we'll aim for January, after the holidays.)

See the preview Redux Toolkit 2.0 + Redux core 5.0 Migration Guide for an overview of breaking changes in RTK 2.0 and Redux core.

The 2.0 integration branch contains the docs preview for the 2.0 changes. Not all changes are documented yet, but you can see API reference pages for most of the new features here:

npm install @reduxjs/toolkit@next

yarn add @reduxjs/toolkit@next

Changelog

Reselect Changes

Reselect v5.0.0-rc.0 makes the breaking change to use weakMapMemoize as the default memoization implementation for createSelector. This memoizer has an effectively infinite cache size, which should cut down on the number of recalculations in a typical app, and help improve performance overall. This is a breaking change, but one that should be invisible to most users.

It also adds a new dev-mode check for result functions that look like x => x, which is almost always a mistake.

See the Reselect v5.0.0-rc.0 release notes for more details.

What's Changed

  • Bump Reselect to 5.0.0-rc.0 to pick up weakMapMemoize change by @markerikson in #3928

Full Changelog: v2.0.0-rc.1...v2.0.0-rc.2

v2.0.0-rc.1

24 Nov 17:05
Compare
Choose a tag to compare
v2.0.0-rc.1 Pre-release
Pre-release

This release candidate updates to the latest Redux 5.0 RC to use its exported isAction and isPlainObject util methods, renames the pre-minified ESM production build to redux-toolkit.browser.mjs and drops the ESM precompiled dev build, and updates build tooling.

Note that we hope to release Redux Toolkit 2.0, Redux core 5.0, and React-Redux 9.0 by the start of December! (If we don't hit that, we'll aim for January, after the holidays.)

See the preview Redux Toolkit 2.0 + Redux core 5.0 Migration Guide for an overview of breaking changes in RTK 2.0 and Redux core.

The 2.0 integration branch contains the docs preview for the 2.0 changes. Not all changes are documented yet, but you can see API reference pages for most of the new features here:

npm install @reduxjs/toolkit@next

yarn add @reduxjs/toolkit@next

Changelog

isAction Predicate

We recently added an isAction predicate to RTK, then realized it's better suited for the Redux core. This can be used anywhere you have a value that could be a Redux action object, and you need to check if it is actually an action. This is specifically useful for use with the updated Redux middleware TS types, where the default value is now unknown and you need to use a type guard to tell TS that the current value is actually an action.

This is now exported from the Redux core, and re-exported from RTK, which also uses it internally to avoid duplicating that logic.

We've also exported the isPlainObject util that's been in the Redux codebase for years as well.

ESM Build Artifacts

We previously dropped the UMD build artifacts in an earlier alpha, but added ESM build artifacts that are pre-compiled to remove the process.env.NODE_ENV definitions, with the intent that these are useable as <script type="module"> tags in the browser. Those were previously named as redux-toolkit.modern.development.mjs and redux-toolkit.modern.production.mjs.

We've renamed the production artifact to redux-toolkit.browser.mjs to be consistent with the other Redux-related packages, and removed the dev build artifact on the grounds that we don't think there's enough likely usage to include it. If you think you would specifically benefit from having an ESM browser-compatible dev artifact, let us know!

What's Changed

Full Changelog: v2.0.0-rc.0...v2.0.0-rc.1

v2.0.0-rc.0

17 Nov 03:50
Compare
Choose a tag to compare
v2.0.0-rc.0 Pre-release
Pre-release

This release candidate modifies the approach for defining async thunks inside of createSlice, and improves several bits of usage and implementation around selectors.

Note that we hope to release Redux Toolkit 2.0, Redux core 5.0, and React-Redux 9.0 by the start of December! (If we don't hit that, we'll aim for January, after the holidays.)

See the preview Redux Toolkit 2.0 + Redux core 5.0 Migration Guide for an overview of breaking changes in RTK 2.0 and Redux core.

The 2.0 integration branch contains the docs preview for the 2.0 changes. Not all changes are documented yet, but you can see API reference pages for most of the new features here:

npm install @reduxjs/toolkit@next

yarn add @reduxjs/toolkit@next

Changelog

Async Thunk and createSlice changes

In earlier alphas, we added the ability to define async thunks directly inside of createSlice.reducers, using a callback syntax. However, that meant that createSlice had a hard dependency on createAsyncThunk, and importing createSlice would always include createAsyncThunk in an app bundle even if it wasn't being used.

In practice, we expect that most RTK users will use createAsyncThunk, either directly or as part of RTK Query. But, we take bundle size seriously, and didn't want to force all uses of createSlice to add the 2K for createAsyncThunk to a bundle if it isn't actually being used.

Since we expect that defining thunks inside of createSlice is a less-common use case, we've settled on a compromise. The standard createSlice method does not support calling create.asyncThunk() inside even if you use the callback syntax. Instead, you need to call buildCreateSlice() to create a customized version of createSlice with the async thunk capabilities built in, and use that:

const createSliceWithThunks = buildCreateSlice({
  creators: { asyncThunk: asyncThunkCreator },
})

const todosSlice = createSliceWithThunks ({
  name: 'todos',
  initialState: {
    loading: false,
    todos: [],
    error: null,
  } as TodoState,
  reducers: (create) => ({
    // A normal "case reducer", same as always
    deleteTodo: create.reducer((state, action: PayloadAction<number>) => {
      state.todos.splice(action.payload, 1)
    }),
    // A case reducer with a "prepare callback" to customize the action
    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)
      }
    ),
    // An async thunk
    fetchTodo: create.asyncThunk(
      // Async payload function as the first argument
      async (id: string, thunkApi) => {
        const res = await fetch(`myApi/todos?id=${id}`)
        return (await res.json()) as Item
      },
      // An object containing `{pending?, rejected?, fulfilled?, settled?, options?}` second
      {
        pending: (state) => {
          state.loading = true
        },
        rejected: (state, action) => {
          state.error = action.payload ?? action.error
        },
        fulfilled: (state, action) => {
          state.todos.push(action.payload)
        },
        // settled is called for both rejected and fulfilled actions
        settled: (state, action) => {
          state.loading = false
        },
      }
    ),
  }),
})

Selector Changes

createSlice now adds a selectSlice field to all slice objects. This simple selector assumes that the slice has been added at rootState[slice.name] (or rootState[slice.reducerPath] if defined). This is useful for basic lookups of the slice's state.

entityAdapter.getSelectors() now accepts alternate selector creators with customized memoization options.

What's Changed

  • [RTK v2.0] output selector fields are currently missing in selector functions created using createDraftSafeSelector. by @aryaemami59 in #3722
  • createDynamicMiddleware bike shedding by @EskiMojo14 in #3763
  • Allow passing selector creators with different memoize options to getSelectors by @EskiMojo14 in #3833
  • Add selectSlice to slice instance by @EskiMojo14 in #3838
  • Throw an error if ApiProvider is nested inside a normal Provider. by @EskiMojo14 in #3855
  • Create standardised methods of modifying reducer handler context. by @EskiMojo14 in #3872
  • Require calling buildCreateSlice to use create.asyncThunk by @EskiMojo14 in #3867
  • Restore the toString override, but keep it out of the docs. by @EskiMojo14 in #3877
  • Selector housekeeping - emplace and unwrapped by @EskiMojo14 in #3878
  • Update deps for RC by @markerikson in #3883

Full Changelog: v2.0.0-beta.4...v2.0.0-rc.0

v2.0.0-beta.4

28 Oct 23:34
Compare
Choose a tag to compare
v2.0.0-beta.4 Pre-release
Pre-release

This beta release updates RTK Query to fix issues around cache entry behavior when subscription: false is used or with multiple lazy queries in progress, alters RTK Query's tag invalidation behavior to better handle cases where multiple invalidations may happen in sequence, rewrites RTK Query's internals to improve performance around subscription data syncing, and updates Reselect to the latest 5.0.0-beta.0.

npm i @reduxjs/toolkit@beta

yarn add @reduxjs/toolkit@beta

The 2.0 integration branch contains the docs preview for the 2.0 changes. Not all changes are documented yet, but you can see API reference pages for most of the new features here:

Changelog

RTK Query Behavior Changes

We've had a number of reports where RTK Query had issues around usage of dispatch(endpoint.initiate(arg, {subscription: false})). There were also reports that multiple triggered lazy queries were resolving the promises at the wrong time. Both of these had the same underlying issue, which was that RTKQ wasn't tracking cache entries in these cases (intentionally). We've reworked the logic to always track cache entries (and remove them as needed), which should resolve those behavior issues.

We also have had issues raised about trying to run multiple mutations in a row and how tag invalidation behaves. RTKQ now has internal logic to delay tag invalidation briefly, to allow multiple invalidations to get handled together. This is controlled by a new invalidationBehavior: 'immediate' | 'delayed' flag on createApi. The new default behavior is 'delayed'. Set it to 'immediate' to revert to the behavior in RTK 1.9.

In RTK 1.9, we reworked RTK Query's internals to keep most of the subscription status inside the RTKQ middleware. The values are still synced to the Redux store state, but this is primarily for display by the Redux DevTools "RTK Query" panel. Related to the cache entry changes above, we've optimized how often those values get synced to the Redux state for perf.

Other Changes

We've updated the Reselect dependency to Reselect 5.0.0-beta.0, which adds the ability to pass memoizer functions and options directly to createSelector.

The new create.asyncThunk() builder inside of createSlice can now be given a settled reducer, which will run when the thunk promise either fulfills or rejects.

What's Changed

Full Changelog: v2.0.0-beta.3...v2.0.0-beta.4