@reduxjs/toolkit
2.13.0 · 29 Sep 2026MIT45.8M / weekRepositoryHomepagenpmAtom feed
72 stable releases, newest first, by release line. Notes are quoted as the maintainers published them.
1.6.2
5 Oct 2021Add extra to BaseQueryApi
Point sourceMappingURL to correct sourcemaps in build artifacts
createAsyncThunkunion return values fall back to allowing only single member
This release fixes several small issues with RTK Query, as well as a regression in the
createAsyncThunktypes and an issue with sourcemap URLs.Changelog
RTK Query Fixes
The
isLoadingflag should only ever betrueon the first run of a hook, but would sometimes briefly flip totrueon later calls. That should now stay the correct value.fetchBaseQueryshould now work properly when used in conjunction withnode-fetch.The
BaseQueryApiobject now correctly includes theextraargument that was provided when configuring the thunk middleware, if any.Other Fixes
Sourcemap URLs should now be correct, especially for the CommonJS build artifacts.
createAsyncThunk's types have been updated to correctly infer return values when working with enums.Lots of assorted docs tweaks and updates!
What's Changed
Add extra to BaseQueryApi
Point sourceMappingURL to correct sourcemaps in build artifacts
createAsyncThunkunion return values fall back to allowing only single memberFix
fetchBaseQueryfor usage withnode-fetchFix "isLoading briefly flips back to
true" #1519
1.6.1
17 Jul 2021fetchBaseQuerynow provides a more meaningful error if the response can't be parsed successfullyfetchBaseQueryhas been tweaked to always readfetchfrom the global scope, rather than closing over it at creation time. This improves usage with test tools that mock or overridefetchat the system level, such as Mirage.
This release improves several edge cases in RTK Query behavior and implementation, deprecates a lesser-used API, and reverts an internal compatability change from 1.6.
Changelog
RTK Query Tweaks
We've made several small tweaks to the RTK Query implementation:
fetchBaseQuerynow provides a more meaningful error if the response can't be parsed successfullyfetchBaseQueryhas been tweaked to always readfetchfrom the global scope, rather than closing over it at creation time. This improves usage with test tools that mock or overridefetchat the system level, such as Mirage.The
skipTokensymbol is now created usingSymbol.for(), to get a consistent referenceAPI slices now warn if you try to add more than one reducer with the same
reducerPathnameAn internal hook usage was tweaked to avoid the "don't call
useLayoutEffecton the server" warning being printed in SSR
Also, mutations no longer track the
originalArgsvalue in the store. That value is needed to re-run queries, but since mutations are not re-run, it wasn't needed. This change resolves cases where users were passing a non-serializable value as the mutation argument and then seeing warnings about it being put into the store.Technically, this is a breaking change (removes a store property what would have been returned by a selector), but it is a necessary bugfix, and it does not appear anyone was actively using that property. So, we're keeping this as a patch release.
Generally, the information removed is still available as:
A property on the promise returned by
dispatchPart of the thunk action
metaReturn value of the
useMutationhook
Other Changes
The typings for
createActionandcreateAsyncThunkhave been tweaked to avoid lint warnings about "unbound methods".The exported version of
getDefaultMiddlewareis now marked as deprecated, and will be removed in a future 2.0 release. Use the function passed as themiddlewarecallback instead, which has the correct store types anyway.In 1.6, we moved the Immer
enableES5plugin init call fromindex.tsto be inside ofcreateReducerinstead, in an effort to maybe save a few bytes for some users. This has caused some issues for users who still support IE11, possibly due to build config issues. Realistically, we expect that everyone who uses RTK will be callingcreateReducer,createSlice, orcreateApiat some point, so there's no real situations where this wouldn't be called anyway. So, we've moved theenableES5call back toindex.tsfor consistency. In a future 2.0 release, we will remove that call entirely, and users that still support IE11 will need to call that themselves.Changes
- deprecated
Call useIsomorphicLayoutEffect to fix warnings (#1319 - @markerikson)
1.6.0
7 Jun 2021Tracking loading state in order to show UI spinners
Avoiding duplicate requests for the same data
Optimistic updates to make the UI feel faster
This release adds the new RTK Query data fetching APIs to Redux Toolkit. It also adds multiple new options to
createAsyncThunkfor includingmetafields and working with results, updates dependencies to Redux 4.1 and Immer 9, and includes a complete rewrite of our build toolchain with additional "modern" build artifacts in the package.While this is a minor release in terms of semver, this is a huge update in terms of functionality, scope, and effort. We're excited about how these new APIs will help our users build better applications with less code and better behavior!
Installation:
npm i @reduxjs/toolkit@latest yarn add @reduxjs/toolkit@latest
Upgrade Note: During the alphas, we received some reports of users seeing incorrect types after installing the RTK 1.6 previews. The problems appeared to be caused by multiple versions of the
reduxpackage ending up in a project'snode_modulesfolder. If you see this issue, you may need to uninstall and reinstallreact-reduxwith the latest version, to help ensure noreduxduplicates are in the package tree.Changelog
RTK Query Data Caching API
RTK Query is a powerful data fetching and caching tool. It is designed to simplify common cases for loading data in a web application, eliminating the need to hand-write data fetching & caching logic yourself.
RTK Query is an optional addon included in the Redux Toolkit package, and its functionality is built on top of the other APIs in Redux Toolkit.
See the RTK Query usage guides and API reference docs for complete information on how to use RTK Query:
redux-toolkit.js.org/rtk-query/overview
Motivation
Web applications normally need to fetch data from a server in order to display it. They also usually need to make updates to that data, send those updates to the server, and keep the cached data on the client in sync with the data on the server. This is made more complicated by the need to implement other behaviors used in today's applications:
Tracking loading state in order to show UI spinners
Avoiding duplicate requests for the same data
Optimistic updates to make the UI feel faster
Managing cache lifetimes as the user interacts with the UI
The Redux core has always been very minimal - it's up to developers to write all the actual logic. That means that Redux has never included anything built in to help solve these use cases. The Redux docs have taught some common patterns for dispatching actions around the request lifecycle to track loading state and request results, and Redux Toolkit's
createAsyncThunkAPI was designed to abstract that typical pattern. However, users still have to write significant amounts of reducer logic to manage the loading state and the cached data.Over the last couple years, the React community has come to realize that "data fetching and caching" is really a different set of concerns than "state management". While you can use a state management library like Redux to cache data, the use cases are different enough that it's worth using tools that are purpose-built for the data fetching use case.
RTK Query takes inspiration from other tools that have pioneered solutions for data fetching, like Apollo Client, React Query, Urql, and SWR, but adds a unique approach to its API design:
The data fetching and caching logic is built on top of Redux Toolkit's
createSliceandcreateAsyncThunkAPIsBecause Redux Toolkit is UI-agnostic, RTK Query's functionality can be used with any UI layer
API endpoints are defined ahead of time, including how to generate query parameters from arguments and transform responses for caching
RTK Query can also generate React hooks that encapsulate the entire data fetching process, provide
dataandisLoadingfields to components, and manage the lifetime of cached data as components mount and unmountRTK Query provides "cache entry lifecycle" options that enable use cases like streaming cache updates via websocket messages after fetching the initial data
We have early working examples of code generation of API slices from OpenAPI and GraphQL schemas
Finally, RTK Query is completely written in TypeScript, and is designed to provide an excellent TS usage experience
Basic Usage
RTK Query is included within the installation of the core Redux Toolkit package. It is available via either of the two entry points below:
import { createApi } from '@reduxjs/toolkit/query' /* React-specific entry point that automatically generates hooks corresponding to the defined endpoints */ import { createApi } from '@reduxjs/toolkit/query/react'For typical usage with React, start by importing
createApiand defining an "API slice" that lists the server's base URL and which endpoints we want to interact with:import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react' import { Pokemon } from './types' // Define a service using a base URL and expected endpoints export const pokemonApi = createApi({ reducerPath: 'pokemonApi', baseQuery: fetchBaseQuery({ baseUrl: 'https://pokeapi.co/api/v2/' }), endpoints: (builder) => ({ getPokemonByName: builder.query<Pokemon, string>({ query: (name) => `pokemon/${name}`, }), }), }) // Export hooks for usage in functional components, which are // auto-generated based on the defined endpoints export const { useGetPokemonByNameQuery } = pokemonApiThe "API slice" also contains an auto-generated Redux slice reducer and a custom middleware that manages suscription lifetimes. Both of those need to be added to the Redux store:
import { configureStore } from '@reduxjs/toolkit' // Or from '@reduxjs/toolkit/query/react' import { setupListeners } from '@reduxjs/toolkit/query' import { pokemonApi } from './services/pokemon' export const store = configureStore({ reducer: { // Add the generated reducer as a specific top-level slice [pokemonApi.reducerPath]: pokemonApi.reducer, }, // Adding the api middleware enables caching, invalidation, polling, // and other useful features of `rtk-query`. middleware: (getDefaultMiddleware) => getDefaultMiddleware().concat(pokemonApi.middleware), }) // optional, but required for refetchOnFocus/refetchOnReconnect behaviors // see `setupListeners` docs - takes an optional callback as the 2nd arg for customization setupListeners(store.dispatch)Finally, import the auto-generated React hooks from the API slice into your component file, and call the hooks in your component with any needed parameters. RTK Query will automatically fetch data on mount, re-fetch when parameters change, provide
{data, isFetching}values in the result, and re-render the component as those values change:import * as React from 'react' import { useGetPokemonByNameQuery } from './services/pokemon' export default function App() { // Using a query hook automatically fetches data and returns query values const { data, error, isLoading } = useGetPokemonByNameQuery('bulbasaur') // render UI based on data and loading state }Bundle Size
RTK Query adds a fixed one-time amount to your app's bundle size. Since RTK Query builds on top of Redux Toolkit and React-Redux, the added size varies depending on whether you are already using those in your app. The estimated min+gzip bundle sizes are:
If you are using RTK already: ~9kb for RTK Query and ~2kb for the hooks.
If you are not using RTK already:
Without React: 17 kB for RTK+dependencies+RTK Query
With React: 19kB + React-Redux, which is a peer dependency
Adding additional endpoint definitions should only increase size based on the actual code inside the
endpointsdefinitions, which will typically be just a few bytes.The functionality included in RTK Query quickly pays for the added bundle size, and the elimination of hand-written data fetching logic should be a net improvement in size for most meaningful applications.
Build Tooling Improvements
We've completely replaced our previous TSDX-based build tooling pipeline with a custom build pipeline based on ESBuild and TypeScript. This should have no visible changes behavior-wise for end users - we've kept the same build artifact names and ES syntax levels. However, it does speed up our own build process, which is important now that we're generating many more output files.
The published package now also includes a set of "modern" ESM build artifacts that target ES2017 syntax instead of ES5. These files should be smaller than the current "ESM" artifact, which is uses the ES module format but with ES5-level syntax for backwards compatibility.
Most bundlers should currently pick up the ESM artifact as the default, such as in Create-React-App projects. If you are planning to drop IE11 compatibility, you should be able to modify your bundler config to import the modern artifact instead. Since the modern artifact includes the usual
process.env.NODE_ENVchecks for build tools to use, we also have pre-compiled versions for "modern dev" and "modern prod" that are suitable for use in browsers or ESM-centric build tools.We've also done an optimization pass on both the RTK core and the RTK Query sections to improve tree shaking.
See this table for details on the generated artifacts, which are available for each of the entry points:
Redux Toolkit Build Artifacts
Filename Module Syntax process.envPurpose <entry-point-name>.cjs.development.jsCJS ES5 'development' Node / dev <entry-point-name>.cjs.production.min.jsCJS ES5 'production' Node / prod <entry-point-name>.esm.jsESM ES5 Embedded Bundler, legacy syntax <entry-point-name>.modern.development.jsESM ES2017 'development' Browser module, dev <entry-point-name>.modern.jsESM ES2017 Embedded Bundler, modern syntax <entry-point-name>.modern.production.min.jsESM ES2017 'production' Browser module, prod <entry-point-name>.umd.jsUMD ES5 'development' Browser script, dev <entry-point-name>.umd.min.jsUMD ES5 'production' Browser script, prod Async Thunk Improvements
We've made several updates to the
createAsyncThunkAPI to support additional flexibility and use cases.Async Thunk
metaSupportcreateAsyncThunkautomatically generates action creators and action types, and then automatically dispatches those actions during execution. This simplifies the process of creating and using thunks, but like all abstractions, also limits flexibility.One limitation has been that there was no way to customize the
metafield to the actions generated bycreateAsyncThunk, and some users needed to add additional metadata to actions for use by other middleware.We've updated
createAsyncThunkto allow adding additional contents tometa. The approach varies based on the action type:pending: there is a newgetPendingMeta({arg, requestId})callback that can be passed as part of thecreateAsyncThunkoptions object. This is necessary because thependingaction is dispatched before the payload creator is even called.fulfilled: there is a newfulfillWithMetautility in the payload creator'sthunkApiobject, which can be used instead of returning the payload directly:return fulfillWithMeta(actualPayload, meta)rejected: the existingrejectWithValueutility now also accepts ametaargument:return rejectWithValue(failedPayload, meta)
Additional Async Thunk Options
createAsyncThunkreturn promises now have a.unwrap()method that returns a promise for the actual result payload, or throws an error if the promise was rejected. This simplifies the use case of working with the thunk result in components.createAsyncThunkalso now accepts anidGeneratoroption to let you swap out the defaultnanoid()ID generation utility for your own, such asuuid4.The action creators attached to each thunk should now be callable without needing access to internal implementation details. This enables using them in your own code and tests if needed.
Dependency Updates
RTK now depends on Redux 4.1, which shrinks bundle size by extracting error messages from the production builds.
We've also updated our Immer dependency to 9.x, which has improved TS types.
See their release notes:
Other Changes
The
miniSerializeErrorutil used bycreateAsyncThunkis now exported from the main package, as is the newcopyWithStructuralSharingutil from the RTK Query entry point.configureStorenow throws errors if themiddlewarearg is undefined, or if the provided middleware array contains undefined values.Changes
There have been far too many changes for us to list here individually :) See the complete lists of PRs in the original RTKQ alpha repo and the PRs merged during the RTK 1.6 integration cycle:
The complete codebase changes can be seen here:
1.5.1
26 Mar 2021Export isDraft from Immer (#944 - @markerikson)
Optimize dev check middleware performance (#943 - @markerikson)
This release updates
createReducer/createSliceto ensure initial state gets frozen, optimizes the performance of the immutability and serializability dev check middleware, and re-exports theoriginalandisDraftAPIs from Immer.Changes
Freezing Initial State
Immer has always frozen its result states in development, and as of Immer 8, does so in production as well. Since both
createReducerandcreateSliceuse Immer, all their result states get frozen.However, the initial state was never being run through Immer, and thus was never being frozen. That meant it was actually possible to mutate initial state values.
We now run the initial state through an Immer no-op reducer, just to ensure that it's properly frozen at all times.
Dev Check Middleware Optimizations
The immutability and serializability dev check middleware act as safety rails to catch common mistakes. However, both of them do so by running deep recursive checks on all of your Redux state, after every dispatched action. Those deep checks are often very expensive, especially as your state grows larger, and the performance can add noticeable lag in dev. RTK offers options for turning them off or ignoring specific slices of state, and warns if they're taking too long, but those are escape hatches we would rather people not use because they lose the safety benefits.
In this release, we've optimized both middleware to run significantly faster.
The immutability middleware now considers frozen objects to be immutable by default, and will stop recursing when it sees a frozen value. This means that in most cases, the runtime of the immutability middleware is effectively 0ms, as it only has to do a few quick checks on your root state slice values without going any deeper. We've also optimized the nested keypath calculation logic, which cuts down on runtime significantly if it does actually have to recurse.
We've also applied the keypath optimizations to the serializability middleware as well. While this middleware can't benefit from the frozen checks, the keypath optimizations have cut its runtime down by about 70% in benchmarks.
Additional Immer Re-Exports
We now export the
originalandisDraftAPIs from Immer for completeness.This release also updates the Immer dependency to 8.0.1+ to address a vulnerability in Immer. Our dependency was already 8.0.0+, so strictly speaking no update was necessary - users just needed to update their own local dependencies to get the newer Immer version.
Docs Updates
We've made several updates to the RTK docs:
Changelog
Export isDraft from Immer (#944 - @markerikson)
Optimize dev check middleware performance (#943 - @markerikson)
Freeze initial state by passing through Immer (#940 - @markerikson)
- security
re-export original from immer (#832- @jackielii)
1.5.0
28 Nov 2020This release updates Immer to v8.x, adds a set of "matcher" utilities to simplify checking if dispatched actions match against a range of known action types, adds additional customization of async thunk error handling, and adds miscellaneous improvements around TS types and some reducer edge cases.
Changes
Immer v8
In RTK v1.4, we upgraded Immer to v7.x, which added a new
currentAPI for debugging.Immer recently released v8.0.0, which changes its default behavior around auto-freezing state values. Previously, Immer only auto-froze data in development mode, partly under the assumption that freezing would be slower overall. Due to internal changes in Immer 7, Immer now checks if data is frozen and can bail out of some of its processing early. As a result, Immer 8 switches to always freezing return values, even in production, Per discussion in the Immer issues linked from the v8 release announcement, this apparently is actually faster on average.
This is a noticeable change in behavior, but we consider it not breaking for RTK on the grounds that you shouldn't be mutating state outside of a reducer anyway, so there shouldn't be any visible effect on correctly-written Redux logic.
We've updated our Immer dependency to v8.x.
Per the Immer docs on auto-freezing, it may be more efficient to shallowly pre-freeze very large data that won't change by using Immer's
freezeutility. RTK now re-exportsfreezeas well.Action Matching Utilities
In RTK v1.4, we added the ability to write "matching reducers" that can respond to more than one potential action based on a predicate function, such as
builder.addMatcher( (action) => action.type.endsWith('/pending'), reducer).Many users have asked for the ability to directly list a series of RTK-generated action creators as possible actions to match against. For RTK 1.5, we're adding several utilities that will help handle that process.
First, we've added
isAnyOf(matchers)andisAllOf(matchers). These effectively perform boolean||and&&checks, and accept an array containing RTK action creators or action-matching predicates. They return a new matching callback that can be passed tobuilder.addMatcher().const isActionSpecial = (action: any): action is SpecialAction => { return action.payload === 'SPECIAL' } const thunkA = createAsyncThunk<string>('a', () => 'result'); // later createSlice({ name, initialState, reducers: {}, extraReducers: (builder) => { builder.addMatcher(isAllOf(isActionSpecial, thunkA.fulfilled), reducer); } })When used with TypeScript,
isAllOfandisAnyOfwill correctly narrow down the possible type ofactionbased on the actions they match against.We've also added a set of matching utilities specifically meant to help check if a given action corresponds to the lifecycle actions dispatched by some specific async thunks.
isPending,isFulfilled,isRejected,isRejectedWithValue, andisAsyncThunkActionall accept an array of thunks generated bycreateAsyncThunk, and will match the corresponding action types from those thunks:const thunkA = createAsyncThunk<string>('a', () => 'result') const thunkB = createAsyncThunk<string>('b', () => 'result') // later createSlice({ name, initialState, reducers: {}, extraReducers: (builder) => { builder.addMatcher(isFulfilled(thunkA, thunkC), reducer); } })They can also be used as standard TS type guards:
if (isFulfilled(action)) { // TS will narrow the type of `action` to match the standard "fulfilled" fields }createAsyncThunkImprovementsWe've fixed a bug in the
unwrapResultutility where it didn't correctly handle use ofrejectWithValue()in the thunk. It will now correctly re-throw the value passed intorejectWithValue().The auto-generated
requestIdis now attached to the promise returned when the thunk is dispatched, as is theargthat was passed into the thunk when it was dispatched.We've added a new
serializeErrorcallback option tocreateAsyncThunk. By default,createAsyncThunkwill convert thrown errors into a serializable equivalent format, but you can override the serialization and provide your own callback if desired.Draft-Safe Selectors
Memoized selectors created with Reselect's
createSelectordon't work well with Immer's Proxy-wrapped draft states, because the selectors typically think the Proxy object is the same reference and don't correctly recalculate results as needed.We've added a
createDraftSafeSelectorAPI that lightly wrapscreateSelectorby checking if the initial argument (usuallystate) is actually an Immer draft value, and if so, callingcurrent(state)to produce a new JS object. This forces the selector to recalculate the results.We've also updated
createEntityAdapter'sgetSelectorsAPI to use these draft-safe selectors.In general, using selectors inside of reducers is an unnecessary abstraction - it's fine to access data like
state.entities[id].value = 123. However, a number of users have expressed an interest in doing so, so we've made these changes to help accommodate that.Other Changes
We now export our internal
isPlainObjectimplementation.If an Immer-powered reducer has
nullas its value, returningundefinedis now accepted.TS types for case reducers have been tweaked to allow returning
Draft<T>to handle an edge case with generics.The TS types for the
devTools.serializeoption inconfigureStorehave been updated to correctly match the actual values.The RTK docs now use a custom Remark plugin created by @phryneas, which allows us to write real TS code for code blocks, compile it to verify it type-checks correctly, and generate a plain JS version of that exact same example, with the TS and JS variations viewable in tabs for each code block.
You can see that in action in pages like the
createSliceAPI docs:redux-toolkit.js.org/api/createSlice
Changelog
Allow to return undefined for null-states in caseReducers (#631 - @phryneas)
Allow CaseReducers to also return
Draft<T>(#756 - @phryneas)Add
requestIdproperty to the promise returned bycreateAsyncThunk(#784 - @phryneas)Export isPlainObject (#795 - @msutkowski)
isAnyOf/isAllOf HOFs for simple matchers (#788 - @douglas-treadwell)
AsyncThunk matchers (#807 - @douglas-treadwell)
Update Immer to 8.x (#821 - @markerikson)
1.4.0
22 Jun 2020builder.addMatcheraccepts a predicate function that looks like(action: Action) => boolean, and a case reducer. If the matcher returnstrue, the case reducer will run. Multiple matchers may be added tocreateReducer, and all matchers that returntruewill run in the order they were defined after any exact case match has run.builder.addDefaultCasewill run a reducer if no other cases or matchers have run.createAsyncThunkcould sometimes skip handling a promise under certain conditions, causing an "Uncaught Promise" warning.
This release updates Immer to v7.x, adds new options for defining "matcher" and "default case" reducers, and adds a new option for adding middleware to the store.
Changes
Immer v7
Immer recently released v7.0.0. The main feature is a new
currentAPI, which takes aProxy-wrapped draft value and returns a finalized snapshot of the draft at that point in time.Logging draft states in RTK reducers has always been difficult, as browsers display proxies in a hard-to-read format. This utility allows a return to logging partially-updated data to see the results, like
console.log(current(state)).We've updated our Immer dependency to v7.x, and now export
currentas part of our public API."Matcher" and "Default Case" Reducers
createReducerhas always been designed to handle exact action types. Both the object form and "builder callback" form let you specify a specific action type string to match against.This is by far the most common use case, but we didn't previously have a way to handle matching a range of possible action types based on some criteria. We also had some requests to add some kind of a "default case" handler, similar to the
defaultkeyword in aswitchstatement.The builder callback form of
createReducernow supports two new methods in addition to the existingbuilder.addCasemethod:builder.addMatcheraccepts a predicate function that looks like(action: Action) => boolean, and a case reducer. If the matcher returnstrue, the case reducer will run. Multiple matchers may be added tocreateReducer, and all matchers that returntruewill run in the order they were defined after any exact case match has run.builder.addDefaultCasewill run a reducer if no other cases or matchers have run.
Example:
const increment = createAction('increment') const decrement = createAction('decrement') createReducer(0, builder => builder .addCase(increment, (state, action) => { // action is inferred correctly here }) // You can chain calls, or have separate `builder.addCase()` lines each time .addCase(decrement, (state, action) => {}) // You can match a range of action types .addMatcher( action => action.endsWith('rejected'), (state, action) => {} ) // and provide a default case if no other handlers matched .addDefaultCase((state, action) => {}) )The new builder methods work the same way in the
extraReducersfield ofcreateSliceas well.Middleware Creation
We already export
getDefaultMiddlewareto allow you to customize the middleware setup when creating the store. However, running[...getDefaultMiddleware, otherMiddleware]often loses type information when used with TypeScript, and middleware types may need to reference the root state type as well.The
middlewareoption forconfigureStorecan now be a callback function that receives a strongly-typed version ofgetDefaultMiddlewareas an argument, and should return the final middleware array. It also includes strongly-typedconcatandprependmethods that preserve type information better than spreading arrays:const store = configureStore({ reducer: rootReducer, middleware: getDefaultMiddleware => getDefaultMiddleware().concat(logger) })Bug Fixes
createAsyncThunkcould sometimes skip handling a promise under certain conditions, causing an "Uncaught Promise" warning.The
updateManyCRUD method ofcreateEntityAdapterwasn't properly merging together multiple changes for the same item ID
Typing Updates
The
conditionargument forcreateAsyncThunkis now defined as returningboolean | undefined. The actual code explicitly checks ifcondition() !== false, so returningundefinedis legal, but the typing was too strict.We now export the return type for
createAsyncThunkfor reuse.Docs Updates
We've extensively updated the
createReducerandcreateSlicepages to cover the new builder methods, andconfigureStoreandgetDefaultMiddlewareto cover the new middleware syntax.We've extracted the info on the immutability and serializability dev check middleware into their own separate API reference pages.
We've added a Usage Guide section on handling "non-serializable value" warnings, including examples of configuration for use with Redux-Persist and React-Redux-Firebase. Related to that, the serializability warning message now includes a link to this section.
The API reference section now has subcategories for "Store Setup", "Reducers and Actions", and "Other".
We've enabled Dark Mode for Docusaurus, including tweaks to colors for better contrast.
Changes
Update docs on serializability usage and dev check middleware (@markerikson - #630)
Add
addMatcherto builder notations &actionMatchersargumen… (@phryneas - #610)Add styling for blockquotes on darkmode (@msutkowski - #615)
Add section to usage guide on working with non-serializable data (@cloeper - #623)
Fixed multiple updates with same id in updateMany (@jakeboone02 - #621)
Bump immer to v7, export current util, add usage docs (@mutkowksi - #604)
Prevent unhandled promises in createAsyncThunk (@msutkowski - #570)
Adding correctly typed
prepend` andconcat` to the array… (@phryneas - #559)Add middlewareBuilder notation with type-curried arguments to co… (@phryneas - #549)
Allow undefined return type for createAsyncThunk options (@melanieseltzer - #595)
1.3.6
9 May 2020Allow nested Immer produce statements with createReducer (@AndrewCraswell - #509)
This release fixes a couple edge cases with Immer usage and reducers, and exposes the
typePrefixfield from thunks generated bycreateAsyncThunk.Changes
Immer Reducer Fixes
The
createEntityAdapterCRUD methods can be used as either standalone reducers (in which case they callcreateNextState()internally) or "mutating" helper functions if given an existing ImmerDraftvalue. However,createReduceralways assumed you were using the reducer standalone.If you were trying to wrap
createReducerand pass in aDraftvalue, changes inside wouldn't be reflected in the externalDraft. We've updatedcreateReducerto check if the incoming state value is actually a `Draft.Also, the
removeAllCRUD method fromcreateEntityAdapterwasn't working correctly when used as a mutating helper, for similar reasons. We've tweaked the logic there to work right.Thunk Type Prefix
createAsyncThunkaccepts atypePrefixstring as its first argument, and uses that to generate thepending/fulfilled/rejectedaction types it dispatches. We had some requests to expose that type string for later usage, so the thunk function now has athunk.typePrefixfield containing that string.Changelog
1.3.5
19 Apr 2020This release adds the ability to cancel async thunks before execution, improves TS thunk types, and fixes a broken middleware option name change.
Changes
Async Thunk Cancellation
The
createAsyncThunkAPI already had support for signaling cancellation of in-progress requests via anAbortController. However, there was no way to skip executing the payload creator callback itself, or skip dispatching thependingaction.We've added a
conditionoption tocreateAsyncThunkthat allows you to run checks before the payload creator is executed, and bail out of the thunk entirely if desired by returningfalse:const fetchUserById = createAsyncThunk( 'users/fetchByIdStatus', async (userId, thunkAPI) => { const response = await userAPI.fetchById(userId) return response.data }, { condition: (userId, { getState, extra }) => { const { users } = getState() const fetchStatus = users.requests[userId] if (fetchStatus === 'fulfilled' || fetchStatus === 'loading') { // Already fetched or in progress, don't need to re-fetch return false } } } )Thunk Typing Improvements
We've updated the
createAsyncThunkTS types to fix an issue when there is no thunk argument, and to make it easier to wrapcreateAsyncThunkin your own abstractions. See #486 / #489 for the issues and #502 / #512 for the updates.Immutable Middleware Options
When we inlined the immutable check middleware, we ended up changing the
ignoreoption name toignoredPaths. We've added handling forignorefor backwards compatibility just in case anyone was relying on that. We've also better documented the options for the serializable check middleware. See #491, #492, and #510 .Changelog
Allow to skip AsyncThunks using a condition callback (@phryneas - #513)
payloadCreator arg argument => asyncThunk arg type (@phryneas - #502)
Update docs, add test, alias ignore->ignoredPaths (@msutkowski - #492)
Add missing parameters to docs for serializable middleware (@msutkowski - #510)
1.3.4
5 Apr 2020Update nanoid and export it (@markerikson - #481)
This release updates our internal
nanoidimplementation, and exports it for general usage.Changes
Export
nanoidThe new
createAsyncThunkAPI we added in v1.3.0 auto-generates a unique request ID every time it's called, so that your reducers can distinguish between separate calls if necessary. To do this, we inlined a copy of thenanoid/non-secureAPI into RTK.The
nanoidlibrary just released a new version, so we've updated our inlined copy to match the implementation ofnanoid/non-secureas of 3.0.2.Since the API is already in the codebase, we've exported it publicly in case it's useful. Usage:
import { nanoid } from '@reduxjs/toolkit' console.log(nanoid()) // 'dgPXxUz_6fWIQBD8XmiSy'Changelog
Update nanoid and export it (@markerikson - #481)
1.3.3
4 Apr 2020Fix/entity types (@markerikson - #477)
This release improves serializability checking in actions, and exports additional types.
Changes
Action Serializability Checks
The serializability check middleware checks the contents of all dispatched actions. When we added
createAsyncThunkin 1.3, we tried to exclude themeta.argspath from those checks, because users may want to pass non-serializable values to their thunks, and the args are automatically added to the actions without the user explicitly putting them there.However, the field name was changed from
meta.argstometa.arglate in development, and the middleware wasn't updated to match, leading to some false positive warnings. We've fixed that, and added additional middleware options for ignoring paths in actions.Type Exports
Per request, we've exported
ThunkDispatchfrom Redux Thunk, and the rest of the internal typedefs related to entities.Changelog
Fix/entity types (@markerikson - #477)
Allow configuration of createSerializableStateInvariantMiddleware ignoredActionPaths, update default value, add tests (@phryneas - #457)
1.3.2
28 Mar 2020When we inlined the immutability check middleware in 1.3.0, we documented the
createImmutableInvariantMiddlewareAPI, but forgot to export it. That's been fixed.When we inlined the immutability check middleware in 1.3.0, we documented the
createImmutableInvariantMiddlewareAPI, but forgot to export it. That's been fixed.Changelog
1.3.1
27 Mar 2020Add basic createEntityAdapter usage docs (@msutkowski - #442)
Add dictionary support to createEntityAdapter many methods (@msutkowski - #444)
This release adds additional argument types for some
createEntityAdapterCRUD methods.Changes
createEntityAdapterInsertion APIscreateEntityAdaptergenerates three methods that can insert entity objects:setAll,addMany, andupsertMany. All three of them accept an array of entities.We expect that a common use case will be to pre-normalize an API response using
normalizr, put the parsed entities into an action, and then handleaction.payload.articlesin a reducer. However, in that case,action.payload.articlesis a pre-normalized object, not an array. While you could doarticlesAdapter.addMany(state, Object.values(action.payload.articles)), we decided to make those three methods accept a normalized object in addition to an array, allowingarticlesAdapter.addMany(state, action.payload.articles)to work correctly.createEntityAdapterUsage Guide DocsWe've also added usage guide examples for
createEntityAdapteras well.Changelog
Add basic createEntityAdapter usage docs (@msutkowski - #442)
Add dictionary support to createEntityAdapter many methods (@msutkowski - #444)
1.3.0
24 Mar 2020This release adds two new APIs:
createEntityAdapterto help manage normalized state, andcreateAsyncThunkto abstract common data fetching behavior.It also improves bundle size by inlining some of our prior dependencies and fixing cases where dev APIs were accidentally being included in production, as well as using a new version of Immer that tree-shakes better.
Finally, we've improved the developer experience by tweaking our TS typings for better inference and updating the dev check middleware to warn if checks are taking too much time.
New APIs
One of the primary goals for Redux Toolkit has always been to simplify common use cases and reduce "boilerplate" by providing APIs that can replace code you were previously writing out by hand.
To that end, v1.3.0 adds two new APIs for the common use cases of async data fetching and managing normalized data in the store.
createAsyncThunkThe Redux docs have taught that async logic should typically dispatch "three-phase async actions" while doing data fetching: a "start" action before the request is made so that loading UI can be displayed, and then a "success" or "failure" action to handle loading the data or showing an error message. Writing these extra action types is tedious, as is writing thunks that dispatch these actions and differ only by what the async request is.
Given that this is a very common pattern, we've added a
createAsyncThunkAPI that abstracts this out. It accepts a base action type string and a callback function that returns a Promise, which is primarily intended to be a function that does a data fetch and returns a Promise containing the results. It then auto-generates the request lifecycle action types / creators, and generates a thunk that dispatches those lifecycle actions and runs the fetching callback.From there, you can listen for those generated action types in your reducers, and handle loading state as desired.
createEntityAdapterThe Redux docs have also advised storing data in a "normalized" state shape, which typically means keeping each type of item in a structure that looks like
{ids:, entities: {} }. However, the Redux core provides no APIs to help manage storing and updating your data using this approach. Many community libraries exist, with varying tradeoffs, but so far we haven't officially recommended any of them.Caching data is a hard problem, and not one that we are interested in trying to solve ourselves. However, given that we do recommend this specific pattern, and that Redux Toolkit is intended to help simplify common use cases, we want to provide a minimal set of functionality to help users manage normalized state.
To help solve this, we've specifically ported the
@ngrx/entitylibrary to work with Redux Toolkit, with some modifications.The core API function is
createEntityAdapter. It generates a set of reducer functions and selectors that know how to work with data that has been stored in that normalized{ids:, entities: {} }format, and can be customized by passing in a function that returns the ID field for a given item. If you want to keep the item IDs in a sorted order, a comparison function can also be passed in.The returned
EntityAdapterobject contains generated CRUD functions for manipulating items within that state, and generated selector functions that know how to read from that state. You can then use the generated CRUD functions and selectors within your own code.There is one very important difference between RTK's implementation and the original
@ngrx/entityimplementation. With@ngrx/entity, methods likeaddOne(item, state)accept the data argument first and the state second. With RTK, the argument order has been flipped, so that the methods look likeaddOne(state, item), and the methods can also accept a standard Redux ToolkitPayloadActioncontaining the data as the second argument. This allows them to be used as Redux case reducers directly, such as passing them in thereducersargument forcreateSlice. They can also be used as "mutating" helper functions insidecreateReducerandcreateSliceas well, thanks to use of Immer internally.Documentation
We've added new API reference and usage guide sections to the Redux Toolkit docs to cover these new APIs:
Bundle Size Improvements and Dependency Updates
Immer 6.0
Immer has always been the largest chunk of code added to your bundle from using RTK. Until now, RTK specifically depended on Immer 4.x, since 5.x added support for handling
Maps andSets (which aren't useful in a Redux app) and that support added to its bundle size.Immer's code was written in a way that kept it from tree-shaking properly. Fortunately, Immer author Michel Weststrate put in some amazing work refactoring the code to better support tree-shaking, and his efforts are now available as Immer 6.0.
Per the Immer documentation on customizing Immer's capabilities, Immer now uses a plugin architecture internally, and additional functionality has to be explicitly enabled as an opt-in. There are currently three Immer plugins that can be enabled: ES5 support (for environments without ES6 Proxies),
Map/Setsupport, and JSON Patch support.Redux Toolkit force-enables ES5 support. This is because we expect RTK to be used in multiple environments that do not support Proxies, such as Internet Explorer and React Native. It's also how Immer previously behaved, so we want to keep that behavior consistent and not break code given that this is a minor release of RTK. (In a hypothetical future major release, we may stop force-enabling the ES5 plugin and ask you to do it if necessary.)
Overall, this should drop a couple KB off your app's minified bundle size.
You may choose to enable the other plugins in your app code if that functionality is desired.
Store Configuration Dependencies
Since its creation, RTK has depended on
leoasis/redux-immutable-state-invariantto throw errors if accidental mutations are detected, and thezalmoxisus/redux-devtools-extensionNPM package to handle setup and configuration of the Redux DevTools Extension as the store is created.Unfortunately, neither of these dependencies is currently published as ES Modules, and we recently found out that the immutable middleware was actually being included in production bundles despite our attempts to ensure it is excluded.
Given that the repo for the immutable middleware has had no activity in the last 3 years, we've opted to fork the package and include the code directly inside Redux Toolkit. We've also inlined the
tiny-invariantandjson-stringify-safepackages that the immutable middleware depended on.The DevTools setup package, while tiny, suffers from the same issue, and so we've forked it as well.
Based on tests locally, these changes should reduce your production bundle sizes by roughly 2.5K minified.
During the development process, we found that the serializable invariant middleware was partly being included in production. We've decided that both the immutable and serializable middleware should always be no-ops in prod if they're ever included, both to ensure minimum bundle size, and to eliminate any unwanted slowdowns.
Other Changes
Type Inference Improvements
Users reported that it was possible to pass an entity adapter update method as a case reducer even if the slice state type didn't match what the update method expected (#434 ). We've updated the TS types to prevent that from being possible.
We've also had a number of cases where users had issues with the typings for action payloads depending on whether
strictNullChecks: falsewas set. We've altered our action creator types to improve that behavior.Dev Check Middleware Timings
The immutability and serializability dev check middleware both do deep checks of state on every dispatch in dev mode. With a large state tree, this can sometimes noticeably slow down the app, and it's not immediately clear that the dev check middleware are responsible for this.
We've updated both middleware to record how much time is spent actually performing the state checks, and they will now log warning messages if the checks take too long to give you a heads-up that you might want to alter the middleware settings or disable them entirely. The delay is configurable, and defaults to 32ms (two UI frames).
In addition, the serializable middleware now ignores
meta.argsin every action by default. This is becausecreateAsyncThunkautomatically takes any arguments to its payload creator function and inserts them into dispatched actions. Since a user may be reasonably passing non-serializable values as arguments, and they're not intentionally inserting those into actions themselves, it seems sensible to ignore any potential non-serializable values in that field.TypeScript Support
We've dropped support for TS versions earlier than 3.5. Given that 3.8 is out, this shouldn't be a major problem for users.
Meanwhile, we've also re-exported the TS types from Reselect for convenience.
Example Usage
This example demonstrates the typical intended usage of both
createEntityAdapterandcreateAsyncThunk.import { createAsyncThunk, createSlice, unwrapResult, createEntityAdapter } from '@reduxjs/toolkit' import { userAPI } from './userAPI' const fetchUserById = createAsyncThunk( 'users/fetchByIdStatus', async (userId) => { const response = await userAPI.fetchById(userId) return response.data } ) const usersAdapter = createEntityAdapter() const usersSlice = createSlice({ name: 'users', initialState: usersAdapter.getInitialState({ loading: 'idle', error: null }), reducers: { usersLoaded: usersAdapter.setAll, userDeleted: usersAdapter.removeOne, }, extraReducers: { [fetchUserById.pending]: (state, action) => { if (state.loading === 'idle') { state.loading = 'pending' } }, [fetchUserById.fulfilled]: (state, action) => { if (state.loading === 'pending') { state.loading = 'idle' usersAdapter.addOne(state, action.payload) } }, [fetchUserById.rejected]: (state, action) => { if (state.loading === 'pending') { state.loading = 'idle' state.error = action.error } } } }) const UsersComponent = () => { const { users, loading, error } = useSelector(state => state.users) const dispatch = useDispatch() const fetchOneUser = async userId => { try { const resultAction = await dispatch(fetchUserById(userId)) const user = unwrapResult(resultAction) showToast('success', `Fetched ${user.name}`) } catch (err) { showToast('error', `Fetch failed: ${err.message}`) } } // render UI here }Changelog
For the complete set of code changes, see:
and this diff:
For the iterative changes as this release was developed, see the Releases page for the individual release notes.
1.2.5
13 Feb 2020This release tweaks the type definitions to fix an error where
metaanderrorcould not be typed when using thepreparenotation ofcreateSlice.Changelog
1.2.4
9 Feb 2020This release tweaks the RTK-specific
ThunkMiddlewaretype definition for better compatibility.Changes
Thunk Middleware Type Definition
In v1.2.2, we improved our type definitions to correctly handle whether or not the thunk middleware was being used.
However, the tutorials and documentation recommended using a type like
type AppThunk = ThunkAction<void, RootState, null, Action<string>>. Thenullfor theextraArgumentoption no longer fit in with the changed types correctly and caused errors with code that was dispatching thunks in some cases.We've tweaked the type definitions to better handle this, and updated the documentation to recommend using a type of
type AppThunk = ThunkAction<void, RootState, unknown, Action<string>>instead.Changelog
1.2.3
19 Jan 2020Add ignoredPaths option to ignore serializability check (@kevin940726 - #320)
This release adds an option to the serializability-check middleware to allow ignoring specific state paths.
Changes
Allow Skipping Serializability Checks for State Paths
The
redux-immutable-state-invariantmiddleware has an option for skipping checks for selected slices of state: github.com/leoasis/redux-immutable-state-invari… . However, our homegrownserializable-state-invariantmiddleware only allowed determining if a given value is serializable, and skipping specific actions, not skipping slices of state.We've now added a
ignoredPathsoption to the serializability middleware that will force it to skip serializability checks for dot-separated keypaths within the state, like:const store = configureStore({ reducer: rootReducer, middleware: getDefaultMiddleware({ serializabilityCheck: { ignoredPaths: ["testSlice.a", "testSlice.b.c"] } }) })Note that we strongly advise against ever putting non-serializable values into the store state. This option is meant only as an escape hatch, such as working around libraries that do that anyway.
Changelog
Add ignoredPaths option to ignore serializability check (@kevin940726 - #320)
1.2.2
16 Jan 2020UMD dev build by removing runtime usage of require statement (@alex-ketch - #317)
This releases fixes our dev UMD build, and improves type inference for
dispatchbased on provided middleware.Changes
Dispatch and Middleware Typings
The Redux core types will modify the type of
dispatchbased on provided middleware, allowing it to accept parameters other than action objects and return other values. Theredux-thunkmiddleware is an example of this.RTK's
configureStoreandgetDefaultMiddlewarewere not correctly picking up the types of the middleware, and were always assuming thatredux-thunkwas enabled at the type level even if the thunk middleware had been disabled.This has been fixed, and the store should now correctly pick up the types of both the default and user-provided middleware.
Additional Type Re-Exports
RTK now re-exports the
ThunkActiontype fromredux-thunk, and theDrafttype fromimmer.Dev UMD fixes
Our dev UMD build was broken due to the recent build configuration changes, and that has now been fixed. This means the sandbox in the Basic Tutorial should be working again.
Changelog
Correctly infer dispatch from provided middlewares (@phryneas - #304)
UMD dev build by removing runtime usage of require statement (@alex-ketch - #317)
1.2.0
26 Dec 2019Mock the console manually (@kevin940726 - #281)
This release rewrites the
createActionandcreateSlicetypes to enable better user readability and reusability, and fixes an issue with the bundling and publishing of the immutable state invariant middleware.(Note: this release was broken due to a missing TS type definition file. Please use v1.2.1 instead.)
Changes
Type Inference Readability Improvements
The type definitions for
createActionandcreateSlicewere primarily written using the TStypekeyword. The TS compiler and inference engine tries to "unwrap" those types, which meant that the inferred type for a variable likeconst test = createAction<number, 'test'>('test')would be shown in an IDE tooltip like this:WithTypeProperty<WithMatch<(<PT extends number>(payload: PT) => WithPayload<PT, Action<"test">>), "test", number, never, never>, "test">
That unwrapped type declaration is hard to read, and not very informative for app developers.
We've rewritten most of our types to use the
interfacekeyword instead. Now, that same variable's inferred type would be displayed as:ActionCreatorWithPayload<number, "test">
This is more informative and easier to read.
Type Export Improvements
Several users had noted that the complexity of the type definitions for
createSlicemade it impossible to write a higher-order or wrapper function in TypeScript that calledcreateSliceinternally ( #276, #286). As part of the typings update, we've refactored the type declarations to expose some public types that can be used to correctly define the arguments that will be passed tocreateSlice, and documented how to wrapcreateSlicein the "Usage with TypeScript" docs page. We've also documented all of the types in the codebase.Module Bundling Fixes
The build tooling setup for RTK tries to deal with several different use cases (dev vs prod, CJS vs ESM vs UMD modules, etc). There were problems with the build config that resulted in a
require()statement being included in the ESM build, and the UMD dev build was actually missing the immutable invariant middleware. The build tooling has been updated to fix those issues.Changelog
Exportable case reducers type, bugfix, documentation (@phryneas - #290)
Refactor actionCreator typings from
typetointerface(@phryneas - #273)Mock the console manually (@kevin940726 - #281)
Use console-testing-library to get consistent snapshots (@kevin940726 - #277)
Fix module bundling & ESM runtime error (@alex-ketch - #280)
1.1.0
1 Dec 2019Inline values in error messages (@kevin940726 - #257)
This release adds a utility function for better type safety with reducer object parameters, and fixes an issue with error message in the serializability check middleware.
Changes
Type-Safe Reducer Object Builder API
createReduceraccepts a plain object full of reducer functions as a parameter, where the keys are the action types that should be handled. While this works fine with plain JS, TypeScript is unable to infer the correct type for the action parameters in each reducer.As an alternative, you may now pass a callback function to
createReducerthat will be given a "builder" object that allows you to add reducers in a type-safe way based on the provided action types:const increment = createAction<number, 'increment'>('increment') const decrement = createAction<number, 'decrement'>('decrement') createReducer(0, builder => builder .addCase(increment, (state, action) => { // action is inferred correctly here }) .addCase(decrement, (state, action: PayloadAction<string>) => { // this would error out }) )While this API is usable from plain JS, it has no real benefit there, and is primarily intended for use with TS.
The same API is also available for the
extraReducersargument ofcreateSlice. It is not necessary for thereducersargument, as the action types are already being defined there.Serialization Error Fixes
Error messages for the serialization check middleware were not correctly displaying the value. This has been fixed.
Docs Updates
Docusaurus v2
Our documentation site at redux-toolkit.js.org has been upgraded to use Docusaurus v2! This comes with a shiny new look and feel, and page loads are now even more Blazing Fast (TM).
New "Usage with TypeScript" Page
We now have a new "Usage with TypeScript" docs page that has examples on how to correctly write and type usage of RTK.
Changelog
Code
Alternative callback-builder-style notation for actionsMap (@phryneas - #262)
Inline values in error messages (@kevin940726 - #257)
Docs
1.0.4
13 Nov 2019As of v1.0.4, we've officially renamed this package from "Redux Starter Kit" to Redux Toolkit!
As of v1.0.4, we've officially renamed this package from "Redux Starter Kit" to Redux Toolkit!
Please switch the installed package name from:
redux-starter-kitto:
Read on for details.
Why A New Name?
The name "Redux Starter Kit" originated from the original discussion issue that started the project, which was titled "RFC: Redux Starter Kit". We originally published it as a personal scoped package during early development (
@acemarke/redux-starter-kit) for ease of management.The plan was always to make it an official Redux-branded package. We had an extensive discussion of possible names. The
redux-starter-kitpackage name was already taken, but the owner donated it to us, and we decided to go with it.The intent behind "Starter Kit" was that "this package is the fastest way to start a Redux project". Unfortunately, as RSK got closer to 1.0, it became apparent that the phrase "Starter Kit" was causing confusion. People told us that they assumed the package was a CLI project creation tool, a cloneable boilerplate, something that was only meant for beginners, or something you would outgrow.
Our goal is that this package should be the default standard way for users to write Redux logic. If people aren't willing to even look at it because of the word "Starter" in the name, then that needed to change.
We put up another naming discussion thread, and concluded that the best options were to A) publish it as a
@reduxjs/scoped package, and B) use the name "Toolkit".So, the final result is a package name of "Redux Toolkit", published on NPM as
@reduxjs/toolkit, and abbrevated as RTK.Our documentation is now published at redux-toolkit.js.org.
Migration
Update all dependencies and imports from
redux-starter-kitto@reduxjs/toolkit, and update the dependency versions to 1.0.4. (There are no code changes from RSK 1.0.1 to RTK 1.0.4 - the new published versions are just README, package naming updates, and fixes for the build setup.)The
redux-starter-kitpackage continues to work as-is as of v1.0.1. However, to encourage migration, we have deprecated all existing RSK versions, and will likely publish a new version of RSK that is empty and will enforce switching to@reduxjs/toolkit.1.0.3
13 Nov 2019Reading the note…