Skip to content

Releases: reduxjs/redux-toolkit

v1.9.4

17 Apr 01:46
Compare
Choose a tag to compare

This bugfix release includes tweaks to RTKQ options handling, tweaks for perf updates, dependency updates, and updates to our CI tooling.

Also, please check out our ongoing RTK 2.0 alpha releases! They have significant improvements to bundle size, ESM/CJS compatibility, TS typings, and reducer update performance. We're looking for real-world feedback on behavior, performance, and any issues you might run into.

Changelog

RTK Query Options Updates

Passing transformResponse as part of enhanceEndpoints can now override the TS type of the original data.

fetchBaseQuery now properly checks for a global responseHandler option.

Performance and Internals

RTK Query now uses Immer's original() to do comparisons inside of copyWithStructuralSharing, which should significantly speed up performance when applying changes from re-fetched data.

RTKQ's internal subscriptionUpdated action is now marked as batchable.

We've updated dependencies to Immer 9.0.21, Reselect 4.1.8, and Redux 4.2.1.

CI Updates

We've added a suite of example apps built with different frameworks such as CRA 4, CRA 5, Next, and Vite, as well as examples that check for compatibility in Node with CJS and ESM modes and with various TS module resolution modes.

What's Changed

Full Changelog: v1.9.3...v1.9.4

v2.0.0-alpha.4

03 Apr 17:53
Compare
Choose a tag to compare
v2.0.0-alpha.4 Pre-release
Pre-release

This is an alpha release for Redux Toolkit 2.0. This release has many changes to our build setup and published package contents, updates the redux and redux-thunk deps to the latest alphas, updates the immer dep to the latest 10.x beta, and has breaking changes.

npm i @reduxjs/toolkit@alpha

yarn add @reduxjs/toolkit@alpha

Also see the redux@5.0.0-alpha.4 release notes.

Changelog

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.

Earlier alphas made changes to the package.json contents and published build artifacts in an attempt to get ESM+CJS compat working correctly, but those alphas had several varying compat issues.

We've set up a battery of example applications in the RTK repo that use a variety of build tools (currently CRA4, CRA5, Next 13, and Vite, Node CJS mode, and Node ESM mode), to verify that Redux and Redux Toolkit compile, import, and run correctly with both TS and various bundlers. We've also set up a check using a custom CLI wrapper around https://arethetypeswrong.github.io to check for potential packaging incompatibilities.

This release changes the names and contents of the published build artifacts, and the various exports/module/main fields in package.json to point to those.

We already tried to point to ESM build artifacts as the default. That should hopefully be be more consistent now.

As of this release, we think we have ESM+CJS compat working correctly, but we ask that the community try out the alphas in your apps and let us know of any compat problems!

Note: The one known potential issue is that TypeScript's new moduleResolution: "node16" mode may see a mismatch between the ESM artifacts and the TS typedefs when imported in a Node CJS environment, and [that may allow hypothetically-incorrect import usage. (See ongoing discussion in https://github.com/arethetypeswrong/arethetypeswrong.github.io/issues/21 .) In practice, we think that probably won't be a concern, and we'll do further investigation before a final release.

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.

For now, we're dropping those build artifacts from the published package, on the grounds that the use cases seem pretty rare today.

We do have browser-ready ESM build artifacts included, which already have process.env.NODE_ENV compiled away for either development or production behavior. These can be loaded via a script tag that points to that file on Unpkg.

If you have strong use cases for us continuing to include UMD build artifacts, please let us know!

Immer 10 Beta

Immer 10 is now in beta. It has several major changes, including faster perf, dropping ES5 environment support, and switching from a default export to only named exports.

We've updated RTK to depend on immer@10.0.0-beta.4.

Performance testing in an artificial RTKQ stress test project showed significant perf increases when dealing with many RTKQ-connected components loading, and that was directly due to Immer-powered reducers executing much faster.

While reducers are not usually the bottleneck in React+Redux apps, this looks like it will be a very nice improvement!

What's Changed

Full Changelog: v2.0.0-alpha.2...v2.0.0-alpha.4

v1.9.3

21 Feb 04:12
Compare
Choose a tag to compare

This release fixes a couple issues with the skip/skipToken options for query hooks, and makes a small perf tweak to serializing query args.

Changelog

Skip Behavior

We made a change in v1.9.0 that tried to make some skip behavior more consistent, including clearing out the cached data. However, we had overlooked that our own docs actually said "skipping a query will keep the cached data", and several users pointed this out as they'd been relying on that behavior.

We've reverted that change. Now, setting {skip: true} or skipToken for a query with existing results will keep the data value (reflecting the last successful query), but currentData will be undefined (reflecting the current settings).

We also identified and fixed an issue that could cause subscription entries to leak under a specific combination of timing and settings changes.

Query Arg Serialization Perf

RTKQ relies on serializing query arguments to serve as the cache keys, with the default using JSON.stringify() + some logic for sorting keys. There was a report that in some apps, large query arg objects could take a while to stringify and this was being done repeatedly. We've added a WeakMap-based cache for query args to avoid re-serializing existing arg values.

What's Changed

Full Changelog: v1.9.2...v1.9.3

v2.0.0-alpha.2

15 Feb 02:12
Compare
Choose a tag to compare
v2.0.0-alpha.2 Pre-release
Pre-release

This alpha release updates the Redux core package dependency to 5.0.0-alpha.2. This release may have breaking changes.

Changelog

Redux v5 Dependency Update

We've updated the redux dependency from 4.2.x to redux@5.0.0-alpha.2. The Redux v5 branch contains the TS conversion work we did in 2019, a migration to full ESM package compatibility, and some additional types changes and internal tweaks.

In practice, these hopefully shouldn't affect most codebases, but that's why these are major versions and we're publishing these alphas :)

We'd like users to try out these alphas releases in your apps and let us know of any build/typing/runtime errors or compatibility problems!

Please see the Redux v5 alpha release notes for further details:

What's Changed

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

v1.9.2

28 Jan 21:21
Compare
Choose a tag to compare

This bugfix release fixes a memory leak in createListenerMiddleware, optimizes performance inside serializableMiddleware, adds new options for fetchBaseQuery, adds support for path RegExp exclusions in serializableMiddleware and immutabilityMiddleware, and improves some TS types.

Changelog

Bug Fixes

createListenerMiddleware had a memory leak that turned out to be due to use of Promise.race(). We've restructured the logic to fix that.

fetchBaseQuery now correctly combines global options with endpoint / default options in all cases.

New Options

fetchBaseQuery now supports a jsonReplacer option that will be used when processing JSON.

Both dev check middleware now support regular expressions in the ignoredPaths array in addition to strings. This adds extra flexibility in skipping certain fields.

TS Changes

The CaseReducer type was sometimes incorrectly inferring its return type in rare cases. That's been fixed.

The isAnyOf/isAllOf matcher function TS types have been tweaked to not require an individual first parameter. This allows spreading arrays of matchers as arguments, like const isLoading = isAnyOf(...interestingPendingThunksArray).

Other Changes

The serializableMiddleware now uses a WeakSet if available to cache values it's seen. This should significantly speed up checks against large state values in development builds.

What's Changed

  • fix CaseReducer to infer from argument, not return value by @phryneas in #3054
  • fetchBaseQuery | Add jsonReplacer param by @tophep in #2904
  • Support RegExp in ignoredPaths/ignoredActionPaths by @markerikson in #3129
  • fix(types): export ThunkWithReturnValue interface by @giomogna in #3108
  • remove unnecessary fetchBaseQuery defaults by @phryneas in #3062
  • make isAnyOf friendly for mapped matchers, but making argument optional by @megagon in #3123
  • raceWithSignal method instead of Promise.race by @phryneas in #3021
  • Fix lint problems and enable linting on CI by @thorn0 in #2992
  • Add caching to serializableStateInvariantMiddleware by @GeorchW in #3115
  • Allow TS isolatedModules flag to be set for safer transpilation by @matmannion in #2911

Full Changelog: v1.9.1...v1.9.2

v2.0.0-alpha.1

21 Jan 03:09
Compare
Choose a tag to compare
v2.0.0-alpha.1 Pre-release
Pre-release

This is an alpha release for Redux Toolkit 2.0. This release has breaking changes.

Changelog

ESM Migration

As a literally-major part of the Redux Toolkit 2.0 alpha development work, we've migrated the package definition to be a full {type: "module"} ESM package (with CJS still included for compatibility purposes).

We've done local testing of the published package in several different build tools and environments:

  • Vite 4
  • Create React App 4 and 5
  • Next 13
  • Parcel 2
  • Node 18 (importing as CJS and ESM)

We have not tested it in React Native projects yet, or other non-React projects.

So far, the package structure seems to work okay in each of those environments, but we ask the community to try out this alpha in your own projects and report any breakages you find!

While it's not strictly related to the ESM migration, one potential compat issue is that we no longer transpile any of our build artifacts (other than removing TS syntax), and we do use the optional chaining operator ( ?. ). It's likely that Webpack 4 will find the correct ESM build artifact, but not be able to parse that syntax. You'll need to either upgrade to Webpack 5, or modify your Webpack config to transpile the RTK build artifacts that are imported from node_modules.

Redux-Thunk 3.0 Alpha

Along with the RTK package, we've migrated redux-thunk to ESM as well and published that as 3.0.0-alpha.1. That release also drops the existing default export, and instead has named exports: import { thunk, withExtraArgument } from 'redux-thunk'.

This should not meaningfully affect RTK users, as configureStore already sets up the thunk middleware for you.

Vitest Migration

We've migrated our own test suite from Jest to Vitest, fixing a couple of compat issues.

Related Future Work

Michel Weststrate, author of Immer, has published a roadmap for Immer 10.0. This includes modernization by dropping non-Proxy ES5 compat, dropping the default export, performance improvements, and more. We plan to include Immer 10 in RTK 2.0.

We're restarting discussion around possible additional changes to Reselect in a v5 major version, and would appreciate feedback on possible improvements there.

See the RTK 2.0 planning thread for other work we're considering for 2.0.

What's Changed

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

v2.0.0-alpha.0

14 Jan 23:31
Compare
Choose a tag to compare
v2.0.0-alpha.0 Pre-release
Pre-release

This is the initial alpha release for Redux Toolkit 2.0. This release has breaking changes.

Please try this out and let us know of any build or runtime incompatibilities beyond the expected breaking changes!

See the RTK 2.0 planning issue for discussion of likely upcoming changes.

Changelog

Removal of Object Object Argument for createReducer and createSlice.extraReducers

As described in the RTK v1.9.0 release notes, we've removed the object form for createReducer and createSlice.extraReducers. Use the "builder callback" form instead.

See the 1.9 release notes for examples of what the necessary changes look like, and details on the available codemod for automatically making this change to your source.

Modernized Build Output Formats

This release drops backwards-compatibility aspects for IE11 in our published package build artifacts:

All module formats (CJS and ESM) now contain code that targets the latest ES spec and no longer backwards-compiled to ES5 syntax

Later 2.0-alpha releases will add full Node ESM support with exports in package.json, and may contain other build artifact changes as well.

Build Tooling Updates

We've updated our own build process to use the latest ESBuild and TS versions.

v1.9.1

30 Nov 05:14
Compare
Choose a tag to compare

This bugfix release fixes assorted issues that were reported with RTK 1.9.0, and adds a few additional requested tweaks and improvements.

Changelog

Fixes

The createAsyncThunk.withTypes function was fully broken (it type-checked correctly, but pointed to the wrong function due to a name shadowing issue). That now works correctly.

The maxRetries option for RTKQ was inadvertently filtering out 0 values, and those are now accepted.

fulfillWithValue had incorrect types that made it appear as if the data was nested an additional level deeper. The types are now correct.

The ActionCreatorWithoutPayload type was tweaked to force an error when an action creator is accidentally called with an argument, which happens in cases like onClick={todoAdded}. This avoids accidentally passing values like React event objects as the payload.

Timer handling for batchActions and autoBatchEnhancer now works in more JS runtime environments.

Other Changes

The TagDescription type is now exported from RTKQ.

API endpoints now have a .name field containing the endpoint name, such as "getPokemon".

Calling promise.abort() on a createAsyncThunk promise before an async condition resolves will now be treated as if the condition itself returned false, bailing out and not dispatching anything.

The merge option now receives a third argument containing {arg, baseQueryMeta, fulfilledTimeStamp, requestId}, in case that info is useful in deciding how to merge.

The @reduxjs/rtk-codemods package has been updated to fix cases where the createSliceBuilder codemod didn't preserve fields with function variable arguments, like [todoAdded]: adapter.addOne. That package has been updated to v0.0.3.

What's Changed

Full Changelog: v1.9.0...v1.9.1

v1.9.0

04 Nov 14:18
Compare
Choose a tag to compare

This feature release adds several new options for RTK Query's createApi and fetchBaseQuery APIs, adds a new upsertQueryData util, rewrites RTKQ's internals for improved performance, adds a new autoBatchEnhancer, deprecates the "object" syntax for createReducer and createSlice.extraReducers, deprecates and removes broken utils for getting running query promises, improves TS inference, exports additional types, and fixes a number of reported issues.

npm i @reduxjs/toolkit@latest

yarn add @reduxjs/toolkit@latest

We plan to start work on RTK 2.0 in the next few weeks. RTK 2.0 will focus on dropping legacy build compatibility and deprecated APIs, with some potential new features. See the linked discussion thread and give us feedback on ideas!

Deprecations and Removals

Object Argument for createReducer and createSlice.extraReducers

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 intend to remove the "object" form for both createReducer and createSlice.extraReducers in RTK 2.0. The builder callback form is effectively the same number of lines of code, and works much better with TypeScript.

Starting with this release, RTK will print a one-time runtime warning for both createReducer and createSlice.extraReducers if you pass in an object argument.

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 for Deprecated Object Reducer Syntax

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

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!

Object reducer codemod before/after examples Before:
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:

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);
  }
})

getRunningOperationPromises Deprecation and Replacement

In v1.7.0, we added an api.util.getRunningOperationPromises() method for use with SSR scenarios, as well as a singular getRunningOperationPromise() method intended for possible use with React Suspense.

Unfortunately, in #2477 we realized that both those methods have a fatal flaw - they do not work with multiple stores in SSR.

As of this release, we are immediately marking getRunningOperationPromises() as deprecated and discouraging its use before we remove it completely in RTK 2.0! It will now throw both runtime and compile errors in development to enforce moving away from using it. However, we are leaving its existing behavior in production builds to avoid actual breakage.

The getRunningOperationPromise() util was experimental, and as far as we can tell not actually being used by anyone, so we are removing getRunningOperationPromise completely in this release.

As replacements, RTKQ now includes four new thunks attached to api.util:

  • getRunningQueryThunk(endpointName, queryArgs)
  • getRunningMutationThunk(endpointName, fixedCacheKeyOrRequestId)
  • getRunningQueriesThunk()
  • getRunningMutationsThunk()

Usages would typically change like this:

-await Promise.all(api.util.getRunningOperationPromises())
+await Promise.all(dispatch(api.util.getRunningQueriesThunk()))

Changelog

New RTK Query createApi Options

createApi endpoints now have several additional options that can be passed in, some of which are intended to work together.

merge Option

RTKQ was built around the assumption that the server is the source of truth, and every refetch replaces the cached data on the client. There are use cases when it would be useful to merge an incoming response into the existing cached data instead, such as pagination or APIs that return varying results over time.

Query endpoints can now accept a merge(cachedData, responseData) callback that lets you do Immer-powered "mutations" to update the existing cached data instead of replacing it entirely.

Since RTKQ assumes that each response per key should replace the existing cache entry by default, the merge option is expected to be used with the serializeQueryArgs and forceRefetch options, as described below.

serializeQueryArgs Option

RTK Query always serializes the cache key value, and uses the string as the actual key for storing the cache entry. The default serialization is the name of the endpoint, plus either the primitive value or a stable-serialized object. An example might be state.api.queries['getPokemon("pikachu")'].

RTKQ already supported customization of this serialization behavior at the createApi level. Now, each endpoint can specify its own serializeQueryArgs method.

The per-endpoint serializeQueryArgs may return either a string, an object, a number, or a boolean. If it's a string, that value will be used as-is. Otherwise, the return value will be run through the default serialization logic. This simplifies the common case of stripping out a couple unwanted object fields from the cache key.

This option serves two main purposes: leaving out values that are passed in to an endpoint but not really part of the "key" conceptually (like a socket or client instance), and altering cache key behavior to use a single entry for the endpoint (such as in an infinite loading / pagination scenario).

Also, the defaultSerializeQueryArgs util is now exported.

    getPost: build.query<Post, { id: string; client: MyApiClient }>({
      queryFn: async ({ id, client }) => {
        const post = await client.fetchPost(id)
        return { data: post }
      },
      serializeQueryArgs: ({ queryArgs, endpointDefinition, endpointName }) => {
        const { id } = queryArgs
        // This can return a string, an object, a number, or a boolean.
        // If it returns an object, number or boolean, that value
        // will be serialized automatically via `defaultSerializeQueryArgs`
        return { id } // omit `client` from the cache key

        // Alternately, you can use `defaultSerializeQueryArgs`:
        // return defaultSerializeQueryArgs({
        //   endpointName,
        //   queryArgs: { id },
        //   endpointDefinition
        // })
        // Or  create and return a string yourself:
        // return `getPost(${id})`
      },
    }),

forceRefresh option

Sometimes you may want to force a refetch, even though RTKQ thinks that the serialized query args haven't changed and there's already a fulfilled cache entry.

This can be used to force RTKQ to actually refetch. One expected use case is an "infinite pagination" scenario where there is one cache entry for the endpoint, different page numbers are given as query args, and the incoming responses are merged into the existing cache entry:

    listItems: build.query<string[], number>({
      query: (pageNumber) => `/listItems?page=${pageNumber}`,
      // Only have one cache entry because the arg always maps to one string
      serializeQueryArgs: ({ endpointName }) => {
        return endpointName
      },
      // Always merge incoming data to the cache entry
      merge: (currentCache, newItems) => {
        currentCache.push(...newItems)
      },
      // Refetch when the page arg changes
      forceRefetch({ currentArg, previousArg }) {
        return currentArg !== previousArg
      },
    }),

transformErrorResponse Option

Similar to transformResponse, endpoints ...

Read more

v1.9.0-rc.1

02 Nov 03:17
Compare
Choose a tag to compare
v1.9.0-rc.1 Pre-release
Pre-release

This release candidate updates the auto-batching enhancer to accept additional options for queuing subscriber notifications, and improves RTKQ perf by removing some unnecessary internal memoized selectors.

Please try this out and give us feedback (even if it's just "tried updating and everything's fine")! If no further issues come up we intend to publish 1.9 in the next few days.

npm i @reduxjs/toolkit@next

yarn add @reduxjs/toolkit@next

Docs updates for 1.9 are complete, and can be viewed here:

https://deploy-preview-2401--redux-starter-kit-docs.netlify.app/

Changelog

Autobatch Enhancer Options

The initial implementation of the autoBatchEnhancer() always queued delayed subscriber notifications using queueMicrotask. We've updated it to accept alternate options that queue with setTimeout, requestAnimationFrame, or bring-your-own-callback (more similar to redux-batched-subscribe).

The variation in JS event loop timing behavior (microtasks, macrotasks, and frames) means having these options may be useful in different situations.

What's Changed

Full Changelog: v1.9.0-rc.0...v1.9.0-rc.1