NewYour coding agent can read the release notes before it upgrades.Set up the MCP server →
npm · #1887 most downloaded on npm
The official, opinionated, batteries-included toolset for efficient Redux development
Last release 4 months ago
15 May 2026
Release timing varies
gaps range from 2 weeks to 5 months
Nearly every release is documented
notes for 58 of the last 60 stable releases
Nothing withdrawn
no release was ever pulled
7 years old
116 releases · first in 2019
One column per quarter.
This feature preview release adds new options and improves behavior for RTK Query, adds a runtime deprecation for the "object" form of createReducer/c…
This feature preview release adds new options and improves behavior for RTK Query, adds a runtime deprecation for the "object" form of createReducer/createSlice.extraReducers, adds the ability to define a "pre-typed" version of createAsyncThunk, improves TS inference of store enhancers, and exports additional TS types.
We hope to add a couple additional RTKQ options as part of the final 1.9 release, including an upsertQueryData util. See the RTK 1.9 milestone for remaining items. No hard ETA yet, but ideally we'd like to publish 1.9 within the next couple weeks if we can wrap things up.
Some highlights for this alpha:
merge OptionRTKQ 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.
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) => {})
}
})
We have initial codemods in the repo that will help rewrite the object form to the builder form, and we'll publish those with instructions alongside 1.9 when it goes final.
When query hooks mount, they dispatch actions to subscribe to the relevant data. The first hook to do so will dispatch a "subscription/fulfilled" action, and all further hooks asking for the same cache key will dispatch "subscription/rejected" actions. Both cause the reducer logic to add another entry to the subscription tracking.
The dispatching of individual "subscription/rejected" actions was causing perf issues when many components mounted at once, due to the number of extra dispatches. RTKQ now batches those into a single combined action per event loop tick, which improves perf for some many-component use cases noticeably.
configureStore now correctly infers changes to the store shape from any store enhancers.
There's now a createAsyncThunk.withTypes() method that can be used to create a "pre-typed" version of createAsyncThunk with types like {state, dispatch, extra} baked in.
isJsonContentType predicate to fetchBaseQuery by @msutkowski in https://github.com/reduxjs/redux-toolkit/pull/2331jsonContentType to fetchBaseQuery options by @msutkowski in https://github.com/reduxjs/redux-toolkit/pull/2403StoreEnhancers by @fostyfost in https://github.com/reduxjs/redux-toolkit/pull/2550Full Changelog: https://github.com/reduxjs/redux-toolkit/compare/v1.8.5...v1.9.0-alpha.0
This bugfix release fixes a couple of issues with RTKQ endpoint tags not invalidating correctly, and tweaks the dispatch type inference to handle more
This bugfix release fixes a couple of issues with RTKQ endpoint tags not invalidating correctly, and tweaks the dispatch type inference to handle more variations of arrays.
dispatch type inference to correctly handle read-only middleware arrays by @dokmic in https://github.com/reduxjs/redux-toolkit/pull/2629Full Changelog: https://github.com/reduxjs/redux-toolkit/compare/v1.8.5...v1.8.6
This bugfix releas fixes an issue with large keepUnusedDataFor values overflowing JS timers, exports the types for the Redux DevTools Extension option
This bugfix releas fixes an issue with large keepUnusedDataFor values overflowing JS timers, exports the types for the Redux DevTools Extension option, and and improves behavior of URL string generation.
keepUnusedDataFor Timer FixkeepUnusedDataFor accepts a value in seconds. When there are no more active subscriptions for a piece of data, RTKQ will set a timer using setTimeout, and keepUnusedDataFor * 1000 as the timer value.
We've been advising users that if they want to keep data in the cache forever that they should use a very large value for keepUnusedDataFor, such as 10 years in seconds.
However, it turns out that JS engines use a 32-bit signed int for timers, and 32-bits in milliseconds is only 24.8 days. If a timer is given a value larger than that, it triggers immediately.
We've updated the internal logic to clamp the keepUnusedDataFor value to be between 0 and THIRTY_TWO_BIT_MAX_TIMER_SECONDS - 1.
Note that in RTK 1.9 (coming soon), RTKQ will also accept Infinity as a special keepUnusedDataFor value to indicate cached data should never be expired.
RTK inlines the TS types for the Redux DevTools Extension options to avoid an extra dependency, but the TS type for the options object wasn't exported publicly. We now export the DevToolsEnhancerOptions type.
The logic for generating a final URL has been updated to avoid adding an extra trailing /.
keepUnusedDataFor values from overflowing setTimeout counter by @markerikson in https://github.com/reduxjs/redux-toolkit/pull/2595Full Changelog: https://github.com/reduxjs/redux-toolkit/compare/v1.8.4...v1.8.5
This bugfix release adds exported TS types for RTKQ hooks for use in wrapping logic, adds useDebugValue to the hooks to improve display in the React D
This bugfix release adds exported TS types for RTKQ hooks for use in wrapping logic, adds useDebugValue to the hooks to improve display in the React DevTools, updates the inlined types for the Redux DevTools options, and fixes an issue in createEntityAdapter that could result in duplicate IDs being stored.
RTK's types heavily rely on inference to minimize the amount of type info users have to provide. However, this can also make it difficult to write functions that wrap calls to RTK APIs.
Some users have asked to have types that help them write "higher-order hooks". RTK now exports types that represent "the return object for a query/mutation hook with a given value": TypedUseQueryHookResult and TypedUseMutationResult. Both require <ResultType, QueryArg, BaseQuery> as generics, like this:
const baseQuery = fetchBaseQuery({url: "https://some.server"});
type CustomHookResult = TypedUseQueryHookResult<MyResultObject, MyArgObject, typeof baseQuery>
const useMyCustomHook = (arg: MyArgObject) : CustomHookResult => {
return api.useGetSomeDataQuery(arg);
}
As of Redux DevTools 3.0, some of field names for custom DevTools options have changed to actionsAllowlist and actionsDenylist. Since we inline the types instead of having a separate dependency, we've updated our TS types to match that. No runtime behavior was changed.
RTKQ hooks now use useDebugValue to give a better preview of the current value in the React DevTools "Component" tab.
The <ApiProvider> component now does a better job of registering and cleaning up focus listeners.
Fixed a bug with createEntityAdapter that could allow duplicate IDs to be added depending on update parameters.
Full Changelog: https://github.com/reduxjs/redux-toolkit/compare/v1.8.3...v1.8.4
This bugfix release fixes a few minor issues and bits of behavior, including updating the React-Redux peer dep to ^8.0.2 final, stable sorting in crea
This bugfix release fixes a few minor issues and bits of behavior, including updating the React-Redux peer dep to ^8.0.2 final, stable sorting in createEntityAdapter.updateMany and some initial state handling in createSlice.
We'd previously published an RTK build that accepted React-Redux v8 beta as a peer dep (for use with RTK Query). Since React-Redux v8 is out now, we've updated the peer dep to ^8.0.2.
Previously, applying updates via createEntityAdapter.updateMany caused sorting order to change. Entities that had the same sorting result should have stayed in the same order relative to each other, but if one of those items had any updates, it would sort to the back of that group. This was due to items being removed from the lookup table and re-added, and since JS engines iterate keys in insertion order, the updated item would now end up compared later than before.
We've reworked the implementation of updateMany to avoid that. This also ended up fixing another issue where multiple update entries targeting the same item ID would only have the first applied.
createSlice Initial StatecreateSlice now logs an error if initialState is undefined. This is most commonly seen when users misspell initialState. It also has better handling for values that can't be frozen by Immer such as primitives.
Several assorted improvements, including TS types for BaseQuery and checking if the body can actually be safely stringified.
updateMany to ensure stable sorting order by @markerikson in https://github.com/reduxjs/redux-toolkit/pull/2464Full Changelog: https://github.com/reduxjs/redux-toolkit/compare/v1.8.2...1.8.3
This bugfix release fixes a minor issue where calling listenerMiddleware.startListening() multiple times with the same effect callback reference would
This bugfix release fixes a minor issue where calling listenerMiddleware.startListening() multiple times with the same effect callback reference would result in multiple entries being added. The correct behavior is that only the first entry is added, and later attempts to add the same effect callback reference just return the existing entry.
Full Changelog: https://github.com/reduxjs/redux-toolkit/compare/v1.8.1...v1.8.2
This release updates RTK's peer dependencies to accept React 18 as a valid version. This should fix installation errors caused by NPM's "install all t
This release updates RTK's peer dependencies to accept React 18 as a valid version. This should fix installation errors caused by NPM's "install all the peer deps and error if they don't match" behavior.
Note: If you are now using React 18, we strongly recommend using the React-Redux v8 beta instead of v7.x!. v8 has been rewritten internally to work correctly with React 18's Concurrent Rendering capabilities. React-Redux v7 will run and generally work okay with existing code, but may have rendering issues if you start using Concurrent Rendering capabilities in your code.
Now that React 18 is out, we plan to finalize React-Redux v8 and release it live within the next couple weeks. We would really appreciate final feedback on using React-Redux v8 beta with React 18 before we publish the final version.
This release adds the new "listener" middleware, updates configureStore's types to better handle type inference from middleware that override dispatch
This release adds the new "listener" middleware, updates configureStore's types to better handle type inference from middleware that override dispatch return values, and updates our TS support matrix to drop support for TS < 4.1.
RTK has integrated the thunk middleware since the beginning. However, thunks are imperative functions, and do not let you run code in response to dispatched actions. That use case has typically been covered with libraries like redux-saga (which handles side effects with "sagas" based on generator functions), redux-observable (which uses RxJS observables), or custom middleware.
We've added a new "listener" middleware to RTK to cover that use case. The listener middleware is created using createListenerMiddleware(), and lets you define "listener" entries that contain an "effect" callback with additional logic and a way to specify when that callback should run based on dispatched actions or state changes.
Conceptually, you can think of this as being similar to React's useEffect hook, except that it runs logic in response to Redux store updates instead of component props/state updates.
The listener middleware is intended to be a lightweight alternative to more widely used Redux async middleware like sagas and observables. While similar to thunks in level of complexity and concept, it can replicate some common saga usage patterns. We believe that the listener middleware can be used to replace most of the remaining use cases for sagas, but with a fraction of the bundle size and a much simpler API.
Listener effect callbacks have access to dispatch and getState, similar to thunks. The listener also receives a set of async workflow functions like take, condition, pause, fork, and unsubscribe, which allow writing more complex async logic.
Listeners can be defined statically by calling listenerMiddleware.startListening() during setup, or added and removed dynamically at runtime with special dispatch(addListener()) and dispatch(removeListener()) actions.
The API reference is available at:
https://redux-toolkit.js.org/api/createListenerMiddleware
Huge thanks to @FaberVitale for major contributions in refining the middleware API and implementing key functionality.
Basic usage of the listener middleware looks like:
import { configureStore, createListenerMiddleware } from '@reduxjs/toolkit'
import todosReducer, {
todoAdded,
todoToggled,
todoDeleted,
} from '../features/todos/todosSlice'
// Create the middleware instance and methods
const listenerMiddleware = createListenerMiddleware()
// Add one or more listener entries that look for specific actions.
// They may contain any sync or async logic, similar to thunks.
listenerMiddleware.startListening({
actionCreator: todoAdded,
effect: async (action, listenerApi) => {
// Run whatever additional side-effect-y logic you want here
console.log('Todo added: ', action.payload.text)
// Can cancel other running instances
listenerApi.cancelActiveListeners()
// Run async logic
const data = await fetchData()
// Pause until action dispatched or state changed
if (await listenerApi.condition(matchSomeAction)) {
// Use the listener API methods to dispatch, get state,
// unsubscribe the listener, start child tasks, and more
listenerApi.dispatch(todoAdded('Buy pet food'))
listenerApi.unsubscribe()
}
},
})
const store = configureStore({
reducer: {
todos: todosReducer,
},
// Add the listener middleware to the store.
// NOTE: Since this can receive actions with functions inside,
// it should go before the serializability check middleware
middleware: (getDefaultMiddleware) =>
getDefaultMiddleware().prepend(listenerMiddleware.middleware),
})
You can use it to write more complex async workflows, including pausing the effect callback until a condition check resolves, and forking "child tasks" to do additional work:
// Track how many times each message was processed by the loop
const receivedMessages = {
a: 0,
b: 0,
c: 0,
}
const eventPollingStarted = createAction('serverPolling/started')
const eventPollingStopped = createAction('serverPolling/stopped')
listenerMiddleware.startListening({
actionCreator: eventPollingStarted,
effect: async (action, listenerApi) => {
// Only allow one instance of this listener to run at a time
listenerApi.unsubscribe()
// Start a child job that will infinitely loop receiving messages
const pollingTask = listenerApi.fork(async (forkApi) => {
try {
while (true) {
// Cancellation-aware pause for a new server message
const serverEvent = await forkApi.pause(pollForEvent())
// Process the message. In this case, just count the times we've seen this message.
if (serverEvent.type in receivedMessages) {
receivedMessages[
serverEvent.type as keyof typeof receivedMessages
]++
}
}
} catch (err) {
if (err instanceof TaskAbortError) {
// could do something here to track that the task was cancelled
}
}
})
// Wait for the "stop polling" action
await listenerApi.condition(eventPollingStopped.match)
pollingTask.cancel()
},
})
configureStore Middleware Type ImprovementsMiddleware can override the default return value of dispatch. configureStore tries to extract any declared dispatch type overrides from the middleware array, and uses that to alter the type of store.dispatch.
We identified some cases where the type inference wasn't working well enough, and rewrote the type behavior to be more correct.
RTK now requires TS 4.1 or greater to work correctly, and we've dropped 4.0 and earlier from our support matrix.
The internal logic for the serializability middleware has been reorganized to allow skipping checks against actions, while still checking values in the state.
Since most of the implementation work on the middleware was done over the last few months, this list only contains the most recent PRs since 1.7.2. For details on the original use case discussions and the evolution of the middleware API over time, see:
PRs since 1.7.2:
Full Changelog: https://github.com/reduxjs/redux-toolkit/compare/v1.7.2...v1.8.0
This preview release adds the new "listener" middleware, and updates configureStore's types to better handle type inference from middleware that overr
This preview release adds the new "listener" middleware, and updates configureStore's types to better handle type inference from middleware that override dispatch return values.
The full 1.8.0 release will be out shortly (within the next couple days), and this RC is primarily for some final compatibility checking. The final release will have a longer changelog description, with examples.
We've been working on a new "listener" middleware, which lets you trigger callback functions when specific actions are dispatched or state is changed.
After iterating on the middleware's API in its own temporary package, it's now ready for actual release as part of RTK.
The preview API reference is available at:
https://deploy-preview-2024--redux-starter-kit-docs.netlify.app/api/createListenerMiddleware
configureStore Middleware Type ImprovementsMiddleware can override the default return value of dispatch. configureStore tries to extract any declared dispatch type overrides from the middleware array, and uses that to alter the type of store.dispatch.
We identified some cases where the type inference wasn't working well enough, and rewrote the type behavior to be more correct.
RTK now requires TS 4.1 or greater to work correctly, and we've dropped 4.0 and earlier from our support matrix.
Full Changelog: https://github.com/reduxjs/redux-toolkit/compare/v1.7.2...v1.8.0-rc.0
This release fixes a TS types bug with RTK Query generated selectors, makes the RTKQ structural sharing behavior configurable, adds an option to have
This release fixes a TS types bug with RTK Query generated selectors, makes the RTKQ structural sharing behavior configurable, adds an option to have the serializability middleware ignore all actions, and has several minor bugfixes and enhancements to RTK Query.
Several users had reported that as of 1.7.0 selectors generated via apiSlice.endpoint.select() were failing to compile when used, with TS errors that looked like Type '{}' is missing the following properties from type 'CombinedState<>.
We've fixed the issue, and selectors should now compile correctly when used with TS.
RTK Query implements a technique called "structural sharing" to preserve existing object references if possible when data for an endpoint is re-fetched. RTKQ recurses over both data structures, and if the contents appear to be the same, keeps the existing values. That helps avoid potential unnecessary re-renders in the UI, because otherwise the entire re-fetched result would be new object references.
However, this update process can potentially take time depending on the size of the response. Endpoints can now be given a structuralSharing option that will turn that off to save on processing time:
const api = createApi({
baseQuery: fetchBaseQuery({ baseUrl: "https://example.com" }),
endpoints: (build) => ({
getEveryEntityInADatabase: build.query({
query: () => ({ url: "/i-cant-paginate-data" }),
structuralSharing: false,
}),
}),
});
Additionally, the serializability check middleware can now be customized with an ignoreActions option to exempt all actions from being checked. This is an escape hatch and isn't recommended for most apps:
const store = configureStore({
reducer: rootReducer,
middleware: (getDefaultMiddleware) =>
getDefaultMiddleware({
serializableCheck: {
ignoreActions: true,
},
}),
});
If an extraArgument was provided to the thunk middleware during store configuration, that value is now passed along to the prepareHeaders() function:
const store = configureStore({
reducer: rootReducer,
middleware: (getDefaultMiddleware) =>
getDefaultMiddleware({
thunk: {
extraArgument: { myCustomApiService },
},
}),
});
// ..later on
const api = createApi({
baseQuery: fetchBaseQuery({
baseUrl: "https://example.com",
prepareHeaders: async (headers, { getState, extra }) => {
const token = getState().auth.token;
const somethingElse = await extra.myCustomApiService.someMethod();
// do things with somethingElse
return headers;
},
}),
});
The invalidatesTags/providesTags functions now receive the action.meta field as an argument, to help with potentially invalidating based on request/response headers.
refetchOnFocus now cleans up cache entries if a focus event is received and there are no active subscriptions, to avoid unnecessary requests.
Active polls are cleaned up when the last component for a given subscription unsubscribes.
The types for builder.addMatcher have been updated to support inference of guards without a type property.
addMatcher typings by @crcarrick in https://github.com/reduxjs/redux-toolkit/pull/1895extra to prepareHeaders, update documentation + tests by @msutkowski in https://github.com/reduxjs/redux-toolkit/pull/1922reducerPath for query definitions by @phryneas in https://github.com/reduxjs/redux-toolkit/pull/1977ignoreActions flag to serializable state middleware by @msutkowski in https://github.com/reduxjs/redux-toolkit/pull/1984structuralSharing on endpoints/queries/createApi by @msutkowski in https://github.com/reduxjs/redux-toolkit/pull/1954Full Changelog: https://github.com/reduxjs/redux-toolkit/compare/v1.7.1...v1.7.2
This release fixes a types issue with RTK 1.7.0 and TS 4.5, as seen in #1829 .
This release fixes a types issue with RTK 1.7.0 and TS 4.5, as seen in #1829 .
Full Changelog: https://github.com/reduxjs/redux-toolkit/compare/v1.7.0...v1.7.1
This feature release has a wide variety of API improvements:
This feature release has a wide variety of API improvements:
currentData field to query resultscondition options in createAsyncThunkcreateSlice/createReducer to accept a "lazy state initializer" functioncreateSlice to avoid potential circular dependency issues by lazy-building its reducernpm i @reduxjs/toolkit@latest
yarn add @reduxjs/toolkit@latest
RTK Query now has support for SSR scenarios, such as the getStaticProps/getServerSideProps APIs in Next.js. Queries can be executed on the server using the existing dispatch(someEndpoint.initiate()) thunks, and then collected using the new await Promise.all(api.getRunningOperationPromises()) method.
API definitions can then provide an extractRehydrationInfo method that looks for a specific action type containing the fetched data, and return the data to initialize the API cache section of the store state.
The related api.util.getRunningOperationPromise() API adds a building block that may enable future support for React Suspense as well, and we'd encourage users to experiment with this idea.
Mutation hooks provide status of in-progress requests, but as originally designed that information was unique per-component - there was no way for another component to see that request status data. But, we had several requests to enable this use case.
useMutation hooks now support a fixedCacheKey option that will store the result status in a common location, so multiple components can read the request status if needed.
This does mean that the data cannot easily be cleaned up automatically, so the mutation status object now includes a reset() function that can be used to clear that data.
Query results now include a currentData field, which contains the latest data cached from the server for the current query arg. Additionally, transformResponse now receives the query arg as a parameter. These can be used to add additional derivation logic in cases when a hooks query arg has changed to represent a different value and the existing data no longer conceptually makes sense to keep displaying.
RTK Query originally only did shallow checks for query arg fields to determine if values had changed. This caused issues with infinite loops depending on user input.
The query hooks now use a "serialized stable value" hook internally to do more consistent comparisons of query args and eliminate those problems.
Also, fetchBaseQuery now supports a paramsSerializer option that allows customization of query string generation from the provided arguments, which enables better interaction with some backend APIs.
The BaseQueryApi and prepareheaders args now include fields for endpoint name, type to indicate if it's a query or mutation, and forced to indicate a re-fetch even if there was already a cache entry. These can be used to help determine headers like Cache-Control: no-cache.
API objects now have a selectInvalidatedBy function that accepts a root state object and an array of query tag objects, and returns a list of details on endpoints that would be invalidated. This can be used to help implement optimistic updates of paginated lists.
Fixed an issue serializing a query arg of undefined. Related, an empty JSON body now is stored as null instead of undefined.
There are now dev warnings for potential mistakes in endpoint setup, like a query function that does not return a data field.
Lazy query trigger promises can now be unwrapped similar to mutations.
Fixed a type error that led the endpoint return type to be erroneously used as a state key, which caused generated selectors to have an inferred state: never argument.
Fixed transformResponse to correctly receive the originalArgs as its third parameter.
api.util.resetApiState will now clear out cached values in useQuery hooks.
The RetryOptions interface is now exported, which resolves a TS build error when using the hooks with TS declarations.
createSlice Lazy Reducers and Circular DependenciesFor the last couple years we've specifically recommended using a "feature folder" structure with a single "slice" file of logic per feature, and createSlice makes that pattern really easy - no need to have separate folders and files for /actions and /constants any more.
The one downside to the "slice file" pattern is in cases when slice A needs to import actions from slice B to respond to them, and slice B also needs to listen to slice A. This circular import then causes runtime errors, because one of the modules will not have finished initializing by the time the other executes the module body. That causes the exports to be undefined, and createSlice throws an error because you can't pass undefined to builder.addCase() in extraReducers. (Or, worse, there's no obvious error and things break later.)
There are well-known patterns for breaking circular dependencies, typically requiring extracting shared logic into a separate file. For RTK, this usually means calling createAction separately, and importing those action creators into both slices.
While this is a rarer problem, it's one that can happen in real usage, and it's also been a semi-frequently listed concern from users who didn't want to use RTK.
We've updated createSlice to now lazily create its reducer function the first time you try to call it. That delay in instantiation should eliminate circular dependencies as a runtime error in createSlice.
createAsyncThunk ImprovementsThe condition option may now be async, which enables scenarios like checking if an existing operation is running and resolving the promise when the other instance is done.
If an idGenerator function is provided, it will now be given the thunkArg value as a parameter, which enables generating custom IDs based on the request data.
The createAsyncThunk types were updated to correctly handle type inference when using rejectWithValue().
createSlice and createReducer now accept a "lazy state initializer" function as the initialState argument. If provided, the initializer will be called to produce a new initial state value any time the reducer is given undefined as its state argument. This can be useful for cases like reading from localStorage, as well as testing.
The isPlainObject util has been updated to match the implementation in other Redux libs.
The UMD builds of RTK Query now attach as window.RTKQ instead of overwriting window.RTK.
Fixed an issue with sourcemap loading due to an incorrect filename replacement.
We've updated our deps to the latest versions:
Dispatch everywhere in the appWe've also lowered RTK's peer dependency on React from ^16.14 to ^16.9, as we just need hooks to be available.
The Redux team has also been working on several other updates to the Redux family of libraries.
We've rewritten React-Redux to add compatibility with the upcoming React 18 release and converted its codebase to TypeScript. It still supports React 16.8+/17 via a /compat entry point. We'd appreciate further testing from the community so we can confirm it works as expected in real apps before final release. For details on the changes, see:
We have been working on a new "action listener middleware" that we hope to release in an upcoming version of RTK. It's designed to let users write code that runs in response to dispatched actions and state changes, including simple callbacks and moderately complex async workflows. The current design appears capable of handling many of the use cases that previously required use of the Redux-Saga or Redux-Observable middlewares, but with a smaller bundle size and simpler API.
The listener middleware is still in alpha, but we'd really appreciate more users testing it out and giving us additional feedback to help us finalize the API and make sure it covers the right use cases.
The RTK Query OpenAPI codegen tool has been rewritten with new options and improved output.
true" #1519 by @phryneas in https://github.com/reduxjs/redux-toolkit/pull/1520arg to transformResponse by @phryneas in https://github.com/reduxjs/redux-toolkit/pull/1521currentData property to hook results. by @phryneas in https://github.com/reduxjs/redux-toolkit/pull/1500useSerializedStableValue for value comparison by @phryneas in https://github.com/reduxjs/redux-toolkit/pull/1533reset method to useMutation hook by @phryneas in https://github.com/reduxjs/redux-toolkit/pull/1476useMutation hook by @phryneas in https://github.com/reduxjs/redux-toolkit/pull/1477useMutation shared results by @Shrugsy in https://github.com/reduxjs/redux-toolkit/pull/1616endpoint, type and forced to BaseQueryApi and prepareHeaders by @phryneas in https://github.com/reduxjs/redux-toolkit/pull/1656AsyncThunkConfig for better inference by @phryneas in https://github.com/reduxjs/redux-toolkit/pull/1644selectInvalidatedBy by @phryneas in https://github.com/reduxjs/redux-toolkit/pull/1665Full Changelog: https://github.com/reduxjs/redux-toolkit/compare/v1.6.2...v1.7.0
This release candidate fixes several assorted small issues and updates dependencies.
This release candidate fixes several assorted small issues and updates dependencies.
Assuming no other problems pop up, we plan on releasing 1.7 in the next couple days.
npm i @reduxjs/toolkit@next
yarn add @reduxjs/toolkit@next
Fixed an issue serializing a query arg of undefined. Related, an empty JSON body now is stored as null instead of undefined.
There are now dev warnings for potential mistakes in endpoint setup, like a query function that does not return a data field.
Lazy query trigger promises can now be unwrapped similar to mutations.
api.util.resetApiState will now clear out cached values in useQuery hooks.
The RetryOptions interface is now exported, which resolves a TS build error when using the hooks with TS declarations.
Updated to Immer ^9.0.7, Reselect ^4.1.5, and Thunk ^2.4.1 to pick up the latest types and bug fixes.
Also, the peer dependencies now list React 18 beta and React-Redux 8 beta as acceptable versions.
The isPlainObject util has been updated to match the implementation in other Redux libs.
The UMD builds of RTK Query now attach as window.RTKQ instead of overwriting window.RTK.
Fixed an issue with sourcemap loading due to an incorrect filename replacement.
Full Changelog: https://github.com/reduxjs/redux-toolkit/compare/v1.7.0-beta.1...v1.7.0-rc.0
This beta release updates createSlice to avoid potential circular dependency issues by lazy-building its reducer, and updates our runtime dependencies
This beta release updates createSlice to avoid potential circular dependency issues by lazy-building its reducer, and updates our runtime dependencies to their latest versions.
npm i @reduxjs/toolkit@next
yarn add @reduxjs/toolkit@next
createSlice Lazy Reducers and Circular DependenciesFor the last couple years we've specifically recommended using a "feature folder" structure with a single "slice" file of logic per feature, and createSlice makes that pattern really easy - no need to have separate folders and files for /actions and /constants any more.
The one downside to the "slice file" pattern is in cases when slice A needs to import actions from slice B to respond to them, and slice B also needs to listen to slice A. This circular import then causes runtime errors, because one of the modules will not have finished initializing by the time the other executes the module body. That causes the exports to be undefined, and createSlice throws an error because you can't pass undefined to builder.addCase() in extraReducers. (Or, worse, there's no obvious error and things break later.)
There are well-known patterns for breaking circular dependencies, typically requiring extracting shared logic into a separate file. For RTK, this usually means calling createAction separately, and importing those action creators into both slices.
While this is a rarer problem, it's one that can happen in real usage, and it's also been a semi-frequently listed concern from users who didn't want to use RTK.
We've updated createSlice to now lazily create its reducer function the first time you try to call it. That delay in instantiation should eliminate circular dependencies as a runtime error in createSlice.
We'd appreciate users trying this out and seeing if it successfully fixes that problem. If you previously extracted some separate actions due to circular dep issues, please try re-consolidating those into the actual slices and see how it works.
We've updated our deps to the latest versions:
Dispatch everywhere in the appWe've also lowered RTK's peer dependency on React from ^16.14 to ^16.9, as we just need hooks to be available.
Full Changelog: https://github.com/reduxjs/redux-toolkit/compare/v1.7.0-beta.0...v1.7.0-beta.1
This release updates RTK Query with support for SSR and rehydration, allows sharing mutation results across components, adds a new currentData field t
This release updates RTK Query with support for SSR and rehydration, allows sharing mutation results across components, adds a new currentData field to query results, adds several new options for customizing endpoints and base queries, adds support for async condition options in createAsyncThunk, and updates createSlice/createReducer to accept a "lazy state initializer" function.
npm i @reduxjs/toolkit@next
yarn add @reduxjs/toolkit@next
See the v1.7 beta docs for updated usage guides and API references:
RTK Query now has support for SSR scenarios, such as the getStaticProps/getServerSideProps APIs in Next.js. Queries can be executed on the server using the existing dispatch(someEndpoint.initiate()) thunks, and then collected using the new await Promise.all(api.getRunningOperationPromises()) method.
API definitions can then provide an extractRehydrationInfo method that looks for a specific action type containing the fetched data, and return the data to initialize the API cache section of the store state.
The related api.util.getRunningOperationPromise() API adds a building block that may enable future support for React Suspense as well, and we'd encourage users to experiment with this idea.
Mutation hooks provide status of in-progress requests, but as originally designed that information was unique per-component - there was no way for another component to see that request status data. But, we had several requests to enable this use case.
useMutation hooks now support a fixedCacheKey option that will store the result status in a common location, so multiple components can read the request status if needed.
This does mean that the data cannot easily be cleaned up automatically, so the mutation status object now includes a reset() function that can be used to clear that data.
Query results now include a currentData field, which contains the latest data cached from the server for the current query arg. Additionally, transformResponse now receives the query arg as a parameter. These can be used to add additional derivation logic in cases when a hooks query arg has changed to represent a different value and the existing data no longer conceptually makes sense to keep displaying.
RTK Query originally only did shallow checks for query arg fields to determine if values had changed. This caused issues with infinite loops depending on user input.
The query hooks now use a "serialized stable value" hook internally to do more consistent comparisons of query args and eliminate those problems.
Also, fetchBaseQuery now supports a paramsSerializer option that allows customization of query string generation from the provided arguments, which enables better interaction with some backend APIs.
The BaseQueryApi and prepareheaders args now include fields for endpoint name, type to indicate if it's a query or mutation, and forced to indicate a re-fetch even if there was already a cache entry. These can be used to help determine headers like Cache-Control: no-cache.
createAsyncThunk ImprovementsThe condition option may now be async, which enables scenarios like checking if an existing operation is running and resolving the promise when the other instance is done.
If an idGenerator function is provided, it will now be given the thunkArg value as a parameter, which enables generating custom IDs based on the request data.
The createAsyncThunk types were updated to correctly handle type inference when using rejectWithValue().
createSlice and createReducer now accept a "lazy state initializer" function as the initialState argument. If provided, the initializer will be called to produce a new initial state value any time the reducer is given undefined as its state argument. This can be useful for cases like reading from localStorage, as well as testing.
API objects now have a selectInvalidatedBy function that accepts a root state object and an array of query tag objects, and returns a list of details on endpoints that would be invalidated. This can be used to help implement optimistic updates of paginated lists.
The Redux team has also recently released Reselect 4.1 and Redux Thunk 2.4. Reselect 4.1 contains major improvements to selector options, including cache sizes > 1, and both libraries have improved TS types. We'll update 1.7 to depend on those new versions before release, but you can update your own projects to make sure you have the new functionality and types available as well:
true" #1519 by @phryneas in https://github.com/reduxjs/redux-toolkit/pull/1520arg to transformResponse by @phryneas in https://github.com/reduxjs/redux-toolkit/pull/1521currentData property to hook results. by @phryneas in https://github.com/reduxjs/redux-toolkit/pull/1500useSerializedStableValue for value comparison by @phryneas in https://github.com/reduxjs/redux-toolkit/pull/1533reset method to useMutation hook by @phryneas in https://github.com/reduxjs/redux-toolkit/pull/1476useMutation hook by @phryneas in https://github.com/reduxjs/redux-toolkit/pull/1477useMutation shared results by @Shrugsy in https://github.com/reduxjs/redux-toolkit/pull/1616endpoint, type and forced to BaseQueryApi and prepareHeaders by @phryneas in https://github.com/reduxjs/redux-toolkit/pull/1656AsyncThunkConfig for better inference by @phryneas in https://github.com/reduxjs/redux-toolkit/pull/1644selectInvalidatedBy by @phryneas in https://github.com/reduxjs/redux-toolkit/pull/1665Full Changelog: https://github.com/reduxjs/redux-toolkit/compare/v1.6.2...v1.7.0-beta.0
This release fixes several small issues with RTK Query, as well as a regression in the createAsyncThunk types and an issue with sourcemap URLs.
This release fixes several small issues with RTK Query, as well as a regression in the createAsyncThunk types and an issue with sourcemap URLs.
The isLoading flag should only ever be true on the first run of a hook, but would sometimes briefly flip to true on later calls. That should now stay the correct value.
fetchBaseQuery should now work properly when used in conjunction with node-fetch.
The BaseQueryApi object now correctly includes the extra argument that was provided when configuring the thunk middleware, if any.
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!
createAsyncThunk union return values fall back to allowing only single member by @phryneas in https://github.com/reduxjs/redux-toolkit/pull/1449fetchBaseQuery for usage with node-fetch by @phryneas in https://github.com/reduxjs/redux-toolkit/pull/1473true" #1519 by @phryneas in https://github.com/reduxjs/redux-toolkit/pull/1520Full Changelog: https://github.com/reduxjs/redux-toolkit/compare/v1.6.1...v1.6.2
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 do…
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.
We've made several small tweaks to the RTK Query implementation:
fetchBaseQuery now provides a more meaningful error if the response can't be parsed successfullyfetchBaseQuery has been tweaked to always read fetch from the global scope, rather than closing over it at creation time. This improves usage with test tools that mock or override fetch at the system level, such as Mirage.skipToken symbol is now created using Symbol.for(), to get a consistent referencereducerPath nameuseLayoutEffect on the server" warning being printed in SSRAlso, mutations no longer track the originalArgs value 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:
dispatchmetauseMutation hookThe typings for createAction and createAsyncThunk have been tweaked to avoid lint warnings about "unbound methods".
The exported version of getDefaultMiddleware is now marked as deprecated, and will be removed in a future 2.0 release. Use the function passed as the middleware callback instead, which has the correct store types anyway.
In 1.6, we moved the Immer enableES5 plugin init call from index.ts to be inside of createReducer instead, 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 calling createReducer, createSlice, or createApi at some point, so there's no real situations where this wouldn't be called anyway. So, we've moved the enableES5 call back to index.ts for 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.
reducerPath (#1252 - @phryneas)getDefaultMiddleware export (#1258 - @Shrugsy)fetch (#1267 - @Shrugsy)enableES5 back in index.ts (#1305 - @komar94)Symbol.for('skipToken') (#1317 - @phryneas)originalArgs (#1318 - @phryneas)This release adds the new RTK Query data fetching APIs to Redux Toolkit. It also adds multiple new options to createAsyncThunk for including meta fiel
This release adds the new RTK Query data fetching APIs to Redux Toolkit. It also adds multiple new options to createAsyncThunk for including meta fields 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.
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:
https://redux-toolkit.js.org/rtk-query/overview
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:
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 createAsyncThunk API 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:
createSlice and createAsyncThunk APIsdata and isLoading fields to components, and manage the lifetime of cached data as components mount and unmountRTK 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 createApi and 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 } = pokemonApi
The "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
}
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:
Adding additional endpoint definitions should only increase size based on the actual code inside the endpoints definitions, 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.
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_ENV checks 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:
<details> <summary><b>Redux Toolkit Build Artifacts</b></summary>
| Filename | Module | Syntax | process.env |
Purpose |
|---|---|---|---|---|
<entry-point-name>.cjs.development.js |
CJS | ES5 | 'development' | Node / dev |
<entry-point-name>.cjs.production.min.js |
CJS | ES5 | 'production' | Node / prod |
<entry-point-name>.esm.js |
ESM | ES5 | Embedded | Bundler, legacy syntax |
<entry-point-name>.modern.development.js |
ESM | ES2017 | 'development' | Browser module, dev |
<entry-point-name>.modern.js |
ESM | ES2017 | Embedded | Bundler, modern syntax |
<entry-point-name>.modern.production.min.js |
ESM | ES2017 | 'production' | Browser module, prod |
<entry-point-name>.umd.js |
UMD | ES5 | 'development' | Browser script, dev |
<entry-point-name>.umd.min.js |
UMD | ES5 | 'production' | Browser script, prod |
| </details> |
We've made several updates to the createAsyncThunk API to support additional flexibility and use cases.
meta SupportcreateAsyncThunk automatically 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 meta field to the actions generated by createAsyncThunk, and some users needed to add additional metadata to actions for use by other middleware.
We've updated createAsyncThunk to allow adding additional contents to meta. The approach varies based on the action type:
pending: there is a new getPendingMeta({arg, requestId}) callback that can be passed as part of the createAsyncThunk options object. This is necessary because the pending action is dispatched before the payload creator is even called.fulfilled: there is a new fulfillWithMeta utility in the payload creator's thunkApi object, which can be used instead of returning the payload directly: return fulfillWithMeta(actualPayload, meta)rejected: the existing rejectWithValue utility now also accepts a meta argument: return rejectWithValue(failedPayload, meta)createAsyncThunk return 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.
createAsyncThunk also now accepts an idGenerator option to let you swap out the default nanoid() ID generation utility for your own, such as uuid4.
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.
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:
The miniSerializeError util used by createAsyncThunk is now exported from the main package, as is the new copyWithStructuralSharing util from the RTK Query entry point.
configureStore now throws errors if the middleware arg is undefined, or if the provided middleware array contains undefined values.
This release has been the work of many contributors working together. We'd like to recognize their efforts here:
We'd also like to thank:
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:
https://github.com/reduxjs/redux-toolkit/compare/v1.5.1...v1.6.0
…those meta fields, and removes fields that were deprecated during the alpha process. We've also fleshed out the RTKQ preview docs with significant new…
This release candidate finalizes the upcoming RTK Query APIs. It adds new options to createAsyncThunk for adding meta fields to thunk-dispatched actions, updates the createApi lifecycle handlers to pass through those meta fields, and removes fields that were deprecated during the alpha process. We've also fleshed out the RTKQ preview docs with significant new content.
This should be the final preview before the RTK 1.6 final release, barring any unforeseen last-minute bugs. (Note: This is the same content as rc.0, but we found a bug in our build process that had wrong output in that release. Hopefully no more issues!)
The preview docs are located at https://deploy-preview-1016--redux-starter-kit-docs.netlify.app .
Installation:
npm i @reduxjs/toolkit@next
yarn add @reduxjs/toolkit@next
meta SupportcreateAsyncThunk automatically 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 meta field to the actions generated by createAsyncThunk, and some users needed to add additional metadata to actions for use by other middleware.
We've updated createAsyncThunk to allow adding additional contents to meta. The approach varies based on the action type:
pending: there is a new getPendingMeta({arg, requestId}) callback that can be passed as part of the createAsyncThunk options object. This is necessary because the pending action is dispatched before the payload creator is even called.fulfilled: there is a new fulfillWithMeta utility in the payload creator's thunkApi object, which can be used instead of returning the payload directly: return fulfillWithMeta(actualPayload, meta)rejected: the existing rejectWithValue utility now also accepts a meta argument: return rejectWithValue(failedPayload, meta)meta SupportThe createApi cache lifecycle callbacks like onCacheEntryAdded now make the meta field available as part of the promise result, such as cacheDataLoaded.
The fields such as onStart and onSuccess that were deprecated in earlier alphas have been removed.
The now-unused ApiWithInjectedEndpoints type was removed.
Various APIs that accept arrays were updated to mark those array parameters as readonly, to help indicate that they shouldn't be mutated, and to ensure that arrays marked as const can be accepted.
The ApiModules type was modified to prevent a inferred type of this node exceeds the maximum length the compiler will serialize TS error.
We've updated to Docusaurus 2.0-beta.0, which includes Webpack 5. No visible change for you, but faster CI builds for us thanks to build artifact caching! (Our docs builds dropped from around 7 minutes each build down to around 25 seconds, thanks to only changed files being rebuilt.)
We've also completed filling out the RTK Query API docs.
meta (#1083 - @phryneas)readonly to array-type function params (#1113 - @phryneas)Id to force lazy evaluation of API type (#1116 - @phryneas)https://github.com/reduxjs/redux-toolkit/compare/v1.6.0-beta.0...v1.6.0-rc.1
This release is broken due to a build misconfiguration - please see `rc.1` instead:
This release is broken due to a build misconfiguration - please see rc.1 instead:
https://github.com/reduxjs/redux-toolkit/releases/tag/v1.6.0-rc.1
remove alpha.2 deprecation fallbacks (#1051 - @phryneas)
This beta release improves the in-progress RTK Query APIs. It adds a new onCacheEntryAdded lifecycle callback to enable streaming cache updates, adds per-endpoint cache timeout overrides and additional options for skipping queries, and fixes issues with query arg serialization and build output, We've also fleshed out the RTKQ preview docs with significant new content.
We are hopeful that this will be the final pre-release with any meaningful API changes, and that the remaining work should just be final polish of the documentation before this goes live.
The preview docs are located at https://deploy-preview-1016--redux-starter-kit-docs.netlify.app .
Installation:
npm i @reduxjs/toolkit@next
yarn add @reduxjs/toolkit@next
RTK Query is built around standard HTTP endpoint request/response-style API calls. However, today's applications often use a hybrid approach, where initial data is fetched via a request, but then further updates are streamed via a websocket or other persistent connection.
Endpoint definitions now support an onCacheEntryAdded lifeycle callback. This callback will be executed whenever a new endpoint subscription entry is added to the cache (ie, when a component requests a specific endpoint+params combination that is not currently being loaded).
The onCacheEntryAdded callback allows you to run additional logic after the initial fetch completes and after the entry is removed from the cache, and provides tools to update the existing cache for this query as needed. The intended use case is to open a websocket-type connection, and update the cached data over time as messages are received. A typical usage might look like:
export const api = createApi({
baseQuery: fetchBaseQuery({ baseUrl: "/" }),
endpoints: (build) => ({
getMessages: build.query({
query: (channel) => `messages/${channel}`,
async onCacheEntryAdded(
arg,
{ updateCachedData, cacheDataLoaded, cacheEntryRemoved }
) {
// wait for the initial query to resolve before proceeding
await cacheDataLoaded;
// Update our query result when messages are received
const unsubscribe = ChatAPI.subscribeToChannel(
arg.channelId,
(message) => {
// Dispatches an update action with the diff
updateCachedData((draft) => {
draft.push(message);
});
}
);
// Clean up when cache subscription is removed
await cacheEntryRemoved;
unsubscribe();
},
}),
}),
});
This adds significant additional flexibility in interacting with cached data.
createApi supports a keepUnusedDataFor option to modify the default cache expiration time, but that can now be overridden on a per-endpoint basis as well.
The selectFromResult option has been reworked so that it receives the standard hook result structure as its input, and you can extract and return whatever pieces of that data are actually needed by this component.
RTKQ now exports a skipToken value that can be passed to queries as an indicator that the query should be skipped for now, in addition to the existing skip flag option. This is primarily useful for working around some TS inference issues with hook return types.
The copyWithStructuralSharing util is now exported.
The updateQueryResult and pathQueryResult util methods have been renamed to updateQueryData and patchQueryData.
Optimistic updates can now be reverted by calling .undo(), which automatically dispatches the appropriate inverse patch update action.
The MiddlewareArray type has been tweaked to produce correct behavior when transpiled.
The default query arg serialization logic now handles nested values correctly.
We've significantly improved the preview RTK Query documentation. We've added pages on "Streaming Updates", "Cached Data", "Customizing Queries", and "Migrating to RTK Query". We've also fleshed out the API references for the generated React hooks, added more details to descriptions of queries and endpoints, and filled out info on how cache lifetime behavior works. Thanks to @Shrugsy for writing most of the docs updates!
copyWithStructuralSharing (#1026 - @phryneas)selectFromResult usage (#1035 - @phryneas)Middleware<any> from casting dispatch to any (#1042 - @phryneas)BaseQueryApi.signal is not optional (#1058 - @phryneas)cacheDataLoaded and queryFulfilled (#1078 - @phryneas)https://github.com/reduxjs/redux-toolkit/compare/v1.6.0-alpha.2...v1.6.0-beta.0
This release fixes a bug with RTK Query cache invalidation, and exposes the useLazyQuerySubscription hook.
This release fixes a bug with RTK Query cache invalidation, and exposes the useLazyQuerySubscription hook.
Installation:
npm i @reduxjs/toolkit@next
yarn add @reduxjs/toolkit@next
We saw that RTK Query cache invalidation was actually broken in alpha.1. After investigation, it looks like TypeScript was outputting incorrect code for looping over a set.values() iterator. We've tweaked the logic to work around that.
Please upgrade to this release ASAP, as invalidation is a key part of RTK Query's functionality.
useLazyQuerySubscriptionThe useLazyQuerySubscription hook was documented, but wasn't actually being included in the output. We've fixed that.
We're still fiddling with the combination of ESBuild and TypeScript for our recently updated build chain, and have flipped a couple more switches in the process. Should be no user-visible difference.
useLazyQuerySubscription for endpoints ( #1017 - @Shrugsy)https://github.com/reduxjs/redux-toolkit/compare/v1.6.0-alpha.1...v1.6.0-alpha.2
We also expect some additional final tweaks to the RTKQ APIs before 1.6 is released, but do not expect any large breaking changes.
This pre-1.6 alpha release integrates the RTK Query source code into the Redux Toolkit repo, publishes the RTK Query APIs as nested endpoints in the @reduxjs/toolkit package, and publishes a docs preview containing the RTK Query API and usage guide docs integrated into the RTK docs site. We've also bumped our Redux dependency to 4.1.0.
This release also contains the changes from 1.6.0-alpha.0, including Immer 9.x and the improvements to createAsyncThunk and createEntityAdapter.
Note: we have published additional bugfixes since alpha.1. Please see the releases list for details and be sure to update to @reduxjs/toolkit@next.
Installation:
npm i @reduxjs/toolkit@next
yarn add @reduxjs/toolkit@next
RTK Query is a data fetching and caching library built for Redux. It's most similar to React Query, Apollo, Urql, and SWR. The idea is that you just say "here's my URL endpoint and some query params", and it handles the entire process of:
So, instead of having to write a bunch of thunks, action creators, reducers, useEffects, and dispatching yourself, you just do:
// src/services/pokemon.ts
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'
// 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({
query: (name: string) => `pokemon/${name}`,
}),
}),
})
// Export hooks for usage in functional components, which are
// auto-generated based on the defined endpoints
export const { useGetPokemonByNameQuery } = pokemonApi
// src/App.tsx
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 logic here
}
We originally developed RTK Query as a standalone package at https://github.com/rtk-incubator/rtk-query in order to enable faster iteration during the alpha process.
Now that the RTK Query APIs have stabilized, we've merged all of the RTK Query source code and docs content back into the main RTK repo. From there, we've updated our build tooling and package publishing to include the RTK Query code inside the @reduxjs/toolkit package, but as separate nested entry points.
// Existing RTK APIs
import { createSlice } from '@reduxjs/toolkit'
// RTK Query APIs, with UI-agnostic logic
import { createApi } from '@reduxjs/toolkit/query'
// Same RTK Query APIs, but with React-specific functionality built in
import { createApi } from '@reduxjs/toolkit/query/react'
If you've been using RTK Query in its standalone alpha package, you should be able to migrate by installing this RTK alpha, and just switching your imports to match the examples above.
Since these are separate entry points from the root package, you won't pay any bundle size cost unless you specifically import the RTK Query APIs.
We have not yet done extensive testing of the merged package - that's why this is an alpha! That said, we've successfully been able to locally publish a preview version of the package and use it in a standalone version of the RTKQ "kitchen sink with React" demo app, which is a standard CRA project. We've also verified that the app builds correctly with both TS 4.1 (including named hooks exports using string literal syntax) and TS 4.0 (with just the per-endpoint hook subfields).
For visualization purposes, here's what the bundle size analysis for that app looks like:
In general, we believe that this alpha should run correctly in varying environments, but we specifically request that users try this out and let us know if you run into any problems.
We also expect some additional final tweaks to the RTKQ APIs before 1.6 is released, but do not expect any large breaking changes.
We've also merged the RTK Query docs content and added it to a preview version of the RTK docs site. We'll leave this preview up during the RTK 1.6 pre-release process. Here's the links to the primary new docs pages:
Note that we still need to fill out additional docs content for the RTK APIs and update some of the examples! We'll be working to finish that before the final release.
Since we just released Redux 4.1.0, this release also bumps our Redux dependency to match that. This will shave about 1K off your existing min+gz bundle sizes.
https://github.com/reduxjs/redux-toolkit/compare/v1.6.0-alpha.0..v1.6.0-alpha.1
…exports key can break Node and is considered a breaking change. We'll look at flipping that on in a future 2.0 release.
This initial pre-1.6 alpha release reworks our build tooling to enable upcoming changes, updates dependencies, adds new entity adapter CRUD methods, and tweaks async thunk options.
We have several major improvements planned for later pre-1.6 alphas, including merging the "RTK Query" APIs back into RTK itself and making those available as subpackages.
We've replaced our existing TSDX build setup with a new custom setup based on ESBuild + TypeScript (thanks to @hardfist for doing all the work on that!). In theory, we should have all of the same build artifacts we had before, targeting the same browser and ES spec levels. We did add a new set of "modern" build artifacts that target ES2017, with builds for bundlers and browwsers. However, we can't officially list those in package.json yet, as apparently defining an exports key can break Node and is considered a breaking change. We'll look at flipping that on in a future 2.0 release.
Please let us know if you see any bugs that may be related to the build tooling and bundling changes!
We've updated Immer to 9.0.x. Also, we've updated Redux to the hot-off-the-presses 4.1.0-alpha.0, which shrinks bundle size by extracted error messages in production. Again, please report any problems you run into.
createEntityAdapter already had methods to add, upsert, and update items. We've added setOne and setMany, which let you add or entirely replace existing items.
createAsyncThunk 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.
createAsyncThunk also now accepts an idGenerator option to let you swap out the default nanoid() ID generation utility for your own, such as uuid4.
We've done a bit of internal cleanup in a few functions to simplify and shorten implementations.
configureStore now checks for accidentally returning undefined for a middleware array, or undefined entries in the array.
The PreloadedState type should now work better with TS 4.3+.
https://github.com/reduxjs/redux-toolkit/compare/v1.5.1...v1.6.0-alpha.0
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…
This release updates createReducer/createSlice to ensure initial state gets frozen, optimizes the performance of the immutability and serializability dev check middleware, and re-exports the original and isDraft APIs from Immer.
Immer has always frozen its result states in development, and as of Immer 8, does so in production as well. Since both createReducer and createSlice use 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.
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.
We now export the original and isDraft APIs 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.
We've made several updates to the RTK docs:
https://github.com/reduxjs/redux-toolkit/compare/v1.5.0...v1.5.1
This 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
This 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.
In RTK v1.4, we upgraded Immer to v7.x, which added a new current API 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 freeze utility. RTK now re-exports freeze as well.
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) and isAllOf(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 to builder.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, isAllOf and isAnyOf will correctly narrow down the possible type of action based 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, and isAsyncThunkAction all accept an array of thunks generated by createAsyncThunk, 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
}
createAsyncThunk ImprovementsWe've fixed a bug in the unwrapResult utility where it didn't correctly handle use of rejectWithValue() in the thunk. It will now correctly re-throw the value passed into rejectWithValue().
The auto-generated requestId is now attached to the promise returned when the thunk is dispatched, as is the arg that was passed into the thunk when it was dispatched.
We've added a new serializeError callback option to createAsyncThunk. By default, createAsyncThunk will convert thrown errors into a serializable equivalent format, but you can override the serialization and provide your own callback if desired.
Memoized selectors created with Reselect's createSelector don'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 createDraftSafeSelector API that lightly wraps createSelector by checking if the initial argument (usually state) is actually an Immer draft value, and if so, calling current(state) to produce a new JS object. This forces the selector to recalculate the results.
We've also updated createEntityAdapter's getSelectors API 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.
We now export our internal isPlainObject implementation.
If an Immer-powered reducer has null as its value, returning undefined is 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.serialize option in configureStore have 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 createSlice API docs:
https://redux-toolkit.js.org/api/createSlice
serialize types (#752 - @blaiz)Draft<T> (#756 - @phryneas)requestId property to the promise returned by createAsyncThunk (#784 - @phryneas)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 t
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.
Immer recently released v7.0.0. The main feature is a new current API, which takes a Proxy-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 current as part of our public API.
createReducer has 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 default keyword in a switch statement.
The builder callback form of createReducer now supports two new methods in addition to the existing builder.addCase method:
builder.addMatcher accepts a predicate function that looks like (action: Action) => boolean, and a case reducer. If the matcher returns true, the case reducer will run. Multiple matchers may be added to createReducer, and all matchers that return true will run in the order they were defined after any exact case match has run.builder.addDefaultCase will 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 extraReducers field of createSlice as well.
We already export getDefaultMiddleware to 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 middleware option for configureStore can now be a callback function that receives a strongly-typed version of getDefaultMiddleware as an argument, and should return the final middleware array. It also includes strongly-typed concat and prepend methods that preserve type information better than spreading arrays:
const store = configureStore({
reducer: rootReducer,
middleware: getDefaultMiddleware => getDefaultMiddleware().concat(logger)
})
createAsyncThunk could sometimes skip handling a promise under certain conditions, causing an "Uncaught Promise" warning.updateMany CRUD method of createEntityAdapter wasn't properly merging together multiple changes for the same item IDThe condition argument for createAsyncThunk is now defined as returning boolean | undefined. The actual code explicitly checks if condition() !== false, so returning undefined is legal, but the typing was too strict.
We now export the return type for createAsyncThunk for reuse.
We've extensively updated the createReducer and createSlice pages to cover the new builder methods, and configureStore and getDefaultMiddleware to 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.
addMatcher to builder notations & actionMatchers argumen… (@phryneas - #610)prepend` and concat` to the array… (@phryneas - #559)https://github.com/reduxjs/redux-toolkit/compare/v1.3.6...v1.4.0
This release fixes a couple edge cases with Immer usage and reducers, and exposes the typePrefix field from thunks generated by createAsyncThunk.
This release fixes a couple edge cases with Immer usage and reducers, and exposes the typePrefix field from thunks generated by createAsyncThunk.
The createEntityAdapter CRUD methods can be used as either standalone reducers (in which case they call createNextState() internally) or "mutating" helper functions if given an existing Immer Draft value. However, createReducer always assumed you were using the reducer standalone.
If you were trying to wrap createReducer and pass in a Draft value, changes inside wouldn't be reflected in the external Draft. We've updated createReducer to check if the incoming state value is actually a `Draft.
Also, the removeAll CRUD method from createEntityAdapter wasn't working correctly when used as a mutating helper, for similar reasons. We've tweaked the logic there to work right.
createAsyncThunk accepts a typePrefix string as its first argument, and uses that to generate the pending/fulfilled/rejected action types it dispatches. We had some requests to expose that type string for later usage, so the thunk function now has a thunk.typePrefix field containing that string.
https://github.com/reduxjs/redux-toolkit/compare/v1.3.5...v1.3.6
This release adds the ability to cancel async thunks before execution, improves TS thunk types, and fixes a broken middleware option name change.
This release adds the ability to cancel async thunks before execution, improves TS thunk types, and fixes a broken middleware option name change.
The createAsyncThunk API already had support for signaling cancellation of in-progress requests via an AbortController. However, there was no way to skip executing the payload creator callback itself, or skip dispatching the pending action.
We've added a condition option to createAsyncThunk that allows you to run checks before the payload creator is executed, and bail out of the thunk entirely if desired by returning false:
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
}
}
}
)
We've updated the createAsyncThunk TS types to fix an issue when there is no thunk argument, and to make it easier to wrap createAsyncThunk in your own abstractions. See #486 / #489 for the issues and #502 / #512 for the updates.
When we inlined the immutable check middleware, we ended up changing the ignore option name to ignoredPaths. We've added handling for ignore for 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 .
https://github.com/reduxjs/redux-toolkit/compare/v1.3.4...v1.3.5
This release updates our internal nanoid implementation, and exports it for general usage.
This release updates our internal nanoid implementation, and exports it for general usage.
nanoidThe new createAsyncThunk API 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 the nanoid/non-secure API into RTK.
The nanoid library just released a new version, so we've updated our inlined copy to match the implementation of nanoid/non-secure as 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'
https://github.com/reduxjs/redux-toolkit/compare/v1.3.3...v1.3.4
This release improves serializability checking in actions, and exports additional types.
This release improves serializability checking in actions, and exports additional types.
The serializability check middleware checks the contents of all dispatched actions. When we added createAsyncThunk in 1.3, we tried to exclude the meta.args path 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.args to meta.arg late 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.
Per request, we've exported ThunkDispatch from Redux Thunk, and the rest of the internal typedefs related to entities.
https://github.com/reduxjs/redux-toolkit/compare/v1.3.2...v1.3.3
When we inlined the immutability check middleware in 1.3.0, we documented the createImmutableInvariantMiddleware API, but forgot to export it. That's
When we inlined the immutability check middleware in 1.3.0, we documented the createImmutableInvariantMiddleware API, but forgot to export it. That's been fixed.
https://github.com/reduxjs/redux-toolkit/compare/v1.3.1...v1.3.2
This release adds additional argument types for some createEntityAdapter CRUD methods.
This release adds additional argument types for some createEntityAdapter CRUD methods.
createEntityAdapter Insertion APIscreateEntityAdapter generates three methods that can insert entity objects: setAll, addMany, and upsertMany. 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 handle action.payload.articles in a reducer. However, in that case, action.payload.articles is a pre-normalized object, not an array. While you could do articlesAdapter.addMany(state, Object.values(action.payload.articles)), we decided to make those three methods accept a normalized object in addition to an array, allowing articlesAdapter.addMany(state, action.payload.articles) to work correctly.
createEntityAdapter Usage Guide DocsWe've also added usage guide examples for createEntityAdapter as well.
https://github.com/reduxjs/redux-toolkit/compare/v1.3.0...v1.3.1
This release adds two new APIs: createEntityAdapter to help manage normalized state, and createAsyncThunk to abstract common data fetching behavior.
This release adds two new APIs: createEntityAdapter to help manage normalized state, and createAsyncThunk to 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.
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 createAsyncThunk API 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/entity library 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 EntityAdapter object 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/entity implementation. With @ngrx/entity, methods like addOne(item, state) accept the data argument first and the state second. With RTK, the argument order has been flipped, so that the methods look like addOne(state, item), and the methods can also accept a standard Redux Toolkit PayloadAction containing the data as the second argument. This allows them to be used as Redux case reducers directly, such as passing them in the reducers argument for createSlice. They can also be used as "mutating" helper functions inside createReducer and createSlice as well, thanks to use of Immer internally.
We've added new API reference and usage guide sections to the Redux Toolkit docs to cover these new APIs:
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 and Sets (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/Set support, 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.
Since its creation, RTK has depended on leoasis/redux-immutable-state-invariant to throw errors if accidental mutations are detected, and the zalmoxisus/redux-devtools-extension NPM 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-invariant and json-stringify-safe packages 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.
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: false was set. We've altered our action creator types to improve that behavior.
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.args in every action by default. This is because createAsyncThunk automatically 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.
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.
This example demonstrates the typical intended usage of both createEntityAdapter and createAsyncThunk.
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
}
We'd like to thank the many people who contributed and made this release possible:
createAsyncThunk that we based our implementation on@ngrx/entity and allowing us to port it to Redux ToolkitcreateAsyncThunk implementationcreateAsyncThunkFor the complete set of code changes, see:
and this diff:
https://github.com/reduxjs/redux-toolkit/compare/v1.2.5...v1.3.0
For the iterative changes as this release was developed, see the Releases page for the individual release notes.
This release improves type inference for action creators and for entity adapters that are used as reducers, adds warnings if dev check middleware are
This release improves type inference for action creators and for entity adapters that are used as reducers, adds warnings if dev check middleware are taking excessive amounts of time, and adds a new selectById entity selector.
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: false was set. We've altered our action creator types to improve that behavior.
Meanwhile, we've also re-exported the TS types from Reselect for convenience.
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).
We've added a selectById selector to createEntityAdapter for convenience.
We've also removed the map() update method, which accepted a callback that returned changes to be applied. It couldn't be used as a reducer (the action would be non-serializable), it's easily re-implemented in userland using the updateMany() update method, and the name map implied a potentially completely different return type of a non-mutating copy, which is not what it actually did.
ActionCreatorWithOptionalPayload (@phryneas - #428)immutableStateInvariantMiddleware (@phryneas - #427)https://github.com/reduxjs/redux-toolkit/compare/v1.3.0-beta.0...v1.3.0-beta.1
This release adds two new APIs: createEntityAdapter to help manage normalized state, and createAsyncThunk to abstract common data fetching behavior.
This release adds two new APIs: createEntityAdapter to help manage normalized state, and createAsyncThunk to abstract common data fetching behavior.
It also improves bundle size by fixing cases where APIs were accidentally being included in production and inlining some of our prior dependencies, as well as using a new version of Immer that tree-shakes better.
Note: this is a beta release. We feel that the APIs are stable and are hopefully ready for a final release soon, and they have been documented and tested. However, we'd still like some additional feedback from users before publishing a final release.
Please comment in issue #373: Roadmap: RTK v1.3.0 with any feedback on using these new APIs.
This version is available as @reduxjs/toolkit@beta on NPM, or @reduxjs/toolkit@1.3.0-beta.0.
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 createAsyncThunk API 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/entity library 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 EntityAdapter object 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/entity implementation. With @ngrx/entity, methods like addOne(item, state) accept the data argument first and the state second. With RTK, the argument order has been flipped, so that the methods look like addOne(state, item), and the methods can also accept a standard Redux Toolkit PayloadAction containing the data as the second argument. This allows them to be used as Redux case reducers directly, such as passing them in the reducers argument for createSlice. They can also be used as "mutating" helper functions inside createReducer and createSlice as well, thanks to use of Immer internally.
See our beta API reference and usage guide docs for details:
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 and Sets (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/Set support, 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.
Since its creation, RTK has depended on leoasis/redux-immutable-state-invariant to throw errors if accidental mutations are detected, and the zalmoxisus/redux-devtools-extension NPM 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-invariant and json-stringify-safe packages 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.
This beta release has a few additional changes since the 1.3.0-alpha.10 release.
createAsyncThunk Error Handling ImprovementsWe've spent a lot of time going over the error handling semantics for createAsyncThunk to ensure that errors can be handled outside the thunk, be correctly typed, and that validation errors from a server can be processed correctly. We also now detect if AbortController is available in the environment, and if not, provide a tiny polyfill that suggests adding a real polyfill to your application.
See PR #393: createAsyncThunk: reject with typed value for much of the discussion and work, as well as the API reference docs.
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, both to ensure minimum bundle size, and to eliminate any unwanted slowdowns.
In addition, the serializable middleware now ignores meta.args in every action by default. This is because createAsyncThunk automatically 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.
We've updated Immer 6 from an alpha build to its final 6.0.1 release. This fixes the ability to use RTK with TS 3.5 and 3.6, as Immer has re-added typings support for those TS versions.
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.
This example demonstrates the typical intended usage of both createEntityAdapter and createAsyncThunk.
import { createAsyncThunk, createSlice, unwrapResult, createEntityAdapter } from '@reduxjs/toolkit'
import { userAPI } from './userAPI'
const fetchUserById = createAsyncThunk(
'users/fetchByIdStatus',
async (userId, { getState }) => {
const { loading } = getState().users
if (loading !== 'idle') {
return
}
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
}
Changes since alpha.10:
meta.args by default (@markerikson - #410)Changes since alpha.10:
https://github.com/reduxjs/redux-toolkit/compare/v1.3.0-alpha.10...v1.3.0-beta.0
All 1.3 changes:
https://github.com/reduxjs/redux-toolkit/compare/v1.2.5...v1.3.0-beta.0
This release reduces bundle sizes by updating to the latest Immer 6 alpha to help reduce bundle sizes and inlining the nanoid dependency.
This release reduces bundle sizes by updating to the latest Immer 6 alpha to help reduce bundle sizes and inlining the nanoid dependency.
Note: Due to the Immer upgrade, this RTK alpha version requires at least TypeScript 3.7 if you are consuming it in a TypeScript app. The final version of RTK 1.3 will hopefully support TS 3.5+ once https://github.com/immerjs/immer/pull/541 has been merged in.
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 and Sets (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 has been hard at work refactoring the code to better support tree-shaking, and his effort are now available as Immer 6.x alpha.
Per the updated Immer alpha installation docs, 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/Set support, and JSON Patch support.
In this alpha, 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.
nanoidWe received reports that the nanoid library, which we used for generating unique request IDs in createAsyncThunk, prints warnings on React Native due to a lack of crypto availability. Since we don't need anything cryptographically secure, just reasonably random, we've inlined the function from nanoid/no-secure and dropped the nanoid dependency.
We've added TypeScript usage guides for createAsyncThunk and createEntityAdapter to the alpha docs:
Alpha docs: createAsyncThunk and createEntityAdapter TypeScript usage guide
https://github.com/reduxjs/redux-toolkit/compare/v1.3.0-alpha.9...v1.3.0-alpha.10
This release reduces bundle sizes by forking and inlining the redux-immutable-state-invariant and redux-devtools-extension dependencies, and modifies
This release reduces bundle sizes by forking and inlining the redux-immutable-state-invariant and redux-devtools-extension dependencies, and modifies the types for createEntityAdapter.
Since its creation, RTK has depended on leoasis/redux-immutable-state-invariant to throw errors if accidental mutations are detected, and the zalmoxisus/redux-devtools-extension NPM 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-invariant and json-stringify-safe packages 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.
As a future note, Immer is currently being reworked to enable better shakeability, and once that is released, we plan on updating RTK to use the new Immer version as well. This is unlikely to make it into RTK 1.3.0, but we'll put out another update once we've been able to confirm that the changes work for us.
createEntityAdapter TypesWe made a few more tweaks to the type definitions for createEntityAdapter. Shouldn't be anything breaking, but clarifying the possible overloads for the generated methods.
any where applicable (@phryneas - #377)https://github.com/reduxjs/redux-toolkit/compare/v1.3.0-alpha.8...v1.3.0-alpha.9
This release fixes a couple edge case bugs with entity update operations, and documents expected update behavior. Also, since the new APIs look to be
This release fixes a couple edge case bugs with entity update operations, and documents expected update behavior. Also, since the new APIs look to be somewhat stabilized, we've merged the original PR into a v1.3.0 tracking branch where we can integrate additional planned changes.
We've put up a v1.3.0 roadmap issue to track other planned work before 1.3.0 is released.
createEntityAdapter Update LogicWe identified a couple potential bugs and edge cases inside the updateMany method implementation. We've fixed a potential issue that might have appeared when multiple updates were passed in attempting to rename the same entity to different IDs in a row, fixed a wrong type definition for comparer callbacks, and documented expected behavior when ID renames do occur.
The alpha docs are available at https://deploy-preview-374--redux-starter-kit-docs.netlify.com/. In particular, see the API references for:
https://github.com/reduxjs/redux-toolkit/compare/v1.2.5...v1.3.0-alpha.8
This release reworks the cancellation functionality in createAsyncThunk.
This release reworks the cancellation functionality in createAsyncThunk.
createAsyncThunk cancellationWe previously added AbortController support to createAsyncThunk, to allow other code to trigger whatever cancellation might be applicable to the async logic inside.
The thunk now exits early when the abort() method is called and doesn't keep waiting for the payloadCreator to finish. This should be an improvement especially in cases where the payloadCreator didn't care about the abort signal, but something outside was awaiting the promise from dispatch.
Also, before this, a non-signal-aware payloadCreator could still finish which would have caused a dispatch of a "fulfilled" action after an "rejected" (abort) action, which was confusing.
We've also removed the meta.abortReason property as it's no longer possible to override the error in the "rejected" action.
https://github.com/reduxjs/redux-toolkit/compare/v1.3.0-alpha.6...v1.3.0-alpha.7
This release alters the internal behavior of the createEntityAdapter CRUD methods to allow them to work as "mutating helper functions" when not direct
This release alters the internal behavior of the createEntityAdapter CRUD methods to allow them to work as "mutating helper functions" when not directly used as case reducers, and adds initial API reference documentation for createEntityAdapter.
createEntityAdapter and ImmerThe original version of createEntityAdapter that we ported from the @ngrx/entity library used hand-written immutable update logic internally. We replaced that with use of Immer, as it made it consistent with how createReducer and createSlice work, and simplified some of the logic.
Previously, the CRUD methods such as addOne() always called Immer's createNextState() internally to wrap the updating logic. This worked okay when the CRUD method was used as a case reducer directly.
However, when called manually as a helper function inside an existing case reducer, the behavior was confusing. The case reducer had already received state as an Immer Draft value, but that draft was being passed into createNextState again. In theory, updates to the nested draft are supposed to propagate back to the parent draft, but we didn't see updates propagating upwards as expected.
We've updated the CRUD methods to first check whether the incoming state value is an Immer Draft, or just a plain JS value. If it's already a Draft, we pass that draft to the mutating internal update logic, so the changes are applied correctly. If it's a plain object, we still call createNextState() and return a plain value.
So, when using the CRUD methods as helper functions, treat them as mutating the existing state value:
const booksSlice = createSlice({
name: "books",
initialState: booksAdapter.getInitialState({totalBooks: 0),
reducers: {
bookAdded(state, action) {
// Ignore return value here, and "mutate" state
booksAdapter.addOne(state, action.payload.book)
// can continue to mutate state here
state.totalBooks++
}
}
})
A first draft of API documentation for createEntityAdapter is now available:
createEntityAdapter draft API reference
https://github.com/reduxjs/redux-toolkit/compare/v1.3.0-alpha.5...v1.3.0-alpha.6
This release reworks the createAsyncThunk types to enable more flexibility in declaring optional generic arguments.
This release reworks the createAsyncThunk types to enable more flexibility in declaring optional generic arguments.
createAsyncThunk Generic TypesPer notes in alpha.4, our original types made it actually impossible to declare the correct State type for use by getState in your promise payload creator callback.
We came up with a stopgap solution, but since TS doesn't allow you to specify individual generic arguments by name, it meant that specifying types for some of the thunk options might require specifying all generic types whether you wanted to or not.
Fortunately, @Ethan-Arrowood has found a novel technique for optionally overriding specific generics by name, in a syntax similar to object destructuring, and we've been able to apply that here.
Per the previous example, the most common use case for createAsyncThunk still does not require specifying any types by hand, as the types for the Returned value and the thunkArg argument will be inferred from your payload creator callback:
// basic usage:
const thunk1 = createAsyncThunk("a", async (arg: string) => {
return 42
})
To specify the type of State, you'll need to specifically declare the types of Returned and thunkArg. Then, pass in an object as the third generic argument, and declare the type of a field named state inside that object:
// specify state type for getState usage
const thunk2 = createAsyncThunk<Promise<number>, string, {state: RootState}>(
"a",
async (arg: string, {getState}) => {
const state = getState();
return 42;
}
)
If you only want to declare the type of the extra thunk argument, do the same thing, but override the extra field instead of state:
interface UserAPI {
fetchUserById: (id: number) => Promise<User>
}
// specify state type for getState usage
const thunk2 = createAsyncThunk<Promise<User>, string, {extra: UserAPI}>(
"a",
async (arg: string, {extra}) => {
const user = await extra.fetchUserById(123)
return user;
}
)
Previously, that would have required also declaring the type of the state generic, since state was listed before extra in the generics definition.
https://github.com/reduxjs/redux-toolkit/compare/v1.3.0-alpha.4...v1.3.0-alpha.5
This alpha release rearranges the TS generic types of createAsyncThunk to fix broken usage.
This alpha release rearranges the TS generic types of createAsyncThunk to fix broken usage.
createAsyncThunk TypescreateAsyncThunk gives you access to getState in the thunkAPI object argument. However, we hadn't tried to exercise that yet in our tests, and it turns out there was no valid way to specify the correct type of the state returned by getState.
We've rearranged the generic types and tweaked the defaults. You should now be able to use it without specifying any generics for the most common case (returning a value, with a potential argument for the payload callback), and specify three types if you need to declare what the state type is:
// basic usage:
const thunk1 = createAsyncThunk("a", async (arg: string) => {
return 42
})
// infers: return = Promise<number>, arg = string
// specify state type for getState usage
const thunk2 = createAsyncThunk<Promise<number>, string, RootState>(
"a",
async (arg: string, {getState}) => {
const state = getState();
return 42;
}
)
// declared: return = Promise<number>, arg = string, state: RootState
We have some ideas for additional potential improvements to these types that may make usage simpler, so please keep an eye out for further alpha releases.
A first draft of API documentation for createAsyncThunk is now available:
createAsyncThunk draft API reference
https://github.com/reduxjs/redux-toolkit/compare/v1.3.0-alpha.3...v1.3.0-alpha.4
See PR #352: Port ngrx/entity and add createAsyncThunk for the complete alpha changes.
This alpha release alters the error-handling behavior of createAsyncThunk (again! :grin: )
This alpha release alters the error-handling behavior of createAsyncThunk (again! :grin: )
createAsyncThunk Error HandlingIn alpha.2, we tried having the thunk always re-throw caught errors. That didn't work out, because they always show up in the browser's console as unhandled exceptions.
Instead, the thunk now always returns a resolved promise containing the last action dispatched, which will be either the fulfilled action or the rejected action. We also export an unwrapAction utility that will either return action.payload if fulfilled or throw action.error if rejected, allowing the dispatching code to chain off the promise if desired:
import { unwrapResult } from '@reduxjs/toolkit'
// in the component
const onClick = () => {
dispatch(fetchUserById(userId))
.then(unwrapResult)
.then(originalPromiseResult => {})
.catch(serializedError => {})
}
Since createAsyncThunk accepts a promise callback, we have no way of knowing if or how the async logic can be canceled. However, we now provide a way for your logic to signal that cancellation needs to occur. An AbortController instance will be created for each request, and abort method will be attached to the returned promise that calls abortController.abort() and rejects the promise. The corresponding abortController.signal object will be passed in to your payload creator callback in the thunkAPI object as thunkAPI.signal, allowing your async logic to check if a cancellation has been requested.
The action.meta field in each action object previously looked like: {args, requestId}
The args field was renamed to arg to indicate it's only one value.
For the pending and fulfilled actions, action.meta is now: {arg, requestId}
The rejected action now includes details on whether the request was aborted, and
action.meta is now: {arg, requestId, aborted, abortReason}
A first draft of API documentation for createAsyncThunk is now available:
createAsyncThunk draft API reference
https://github.com/reduxjs/redux-toolkit/compare/v1.3.0-alpha.2...v1.3.0-alpha.3
See PR #352: Port ngrx/entity and add createAsyncThunk for the complete alpha changes.
This alpha release alters the error-handling behavior of createAsyncThunk.
This alpha release alters the error-handling behavior of createAsyncThunk.
createAsyncThunk Error HandlingcreateAsyncThunk catches rejected promises from the promise payload callback, and dispatches a "rejected" action in response with the error value. That means that the thunk itself always returns a resolved promise.
We had a request to re-throw errors from the thunk, in case the calling code wants to chain off the promise or handle it further, so we've implemented that.
We've also removed the "finished" action that was previously being dispatched in a finally {} clause at the end, as it shouldn't truly be necessary - app logic should just need to respond to either the "fulfilled" or "rejected" actions.
When an error is caught in the thunk, we try to put it into the "rejected" action. But, since JS Error objects aren't actually serializable, we now check for Error objects and serialize them into plain JS objects with any of the relevant fields ( {name?, message?, stack?, code?}), or just the value itself if it's not an Error. Unfortunately, since the err value in a catch(err) {} clause doesn't have a type, the action.error field will come through as a type of any either way.
Finally, we've reordered the logic inside to avoid cases where an error while dispatching "success" gets swallowed and triggers the "AJAX failed" handling.
https://github.com/reduxjs/redux-toolkit/compare/v1.3.0-alpha.1...v1.3.0-alpha.2
This release makes several noticeable changes to the alpha createEntityAdapter and createAsyncThunk APIs that were introduced in v1.3.0-alpha.0.
This release makes several noticeable changes to the alpha createEntityAdapter and createAsyncThunk APIs that were introduced in v1.3.0-alpha.0.
createEntityAdapterWe made several changes to the type definitions for EntityAdapter and its related types:
string and number IDs with a single type EntityId = string | number type, and used that everywhereEntityAdapter method type overloads for correct inference of PayloadAction<T> when passed directly as a case reducer inside of createSlice's reducers fieldremoveMany(Predicate) overload, as we discourage passing functions inside of actionscreateAsyncThunkThe alpha.0 release was broken when used with TypeScript. If you tried to declare a promise payload callback that took no parameters, our initial types forced TS to assume that the actual thunk action creator took a single arg of type never, making it impossible to dispatch correctly.
The types that we started from also were overly complex in how they tried to infer arguments and the return value of the promise callback.
We've reworked the createAsyncThunk types to correctly allow declaring a promise callback with no arguments, and simplified the type inference when there are arguments.
Previously, the result of the promise callback and the arguments to the thunk action creator were passed together in the payload, as payload: {result, args}
This makes it harder to combine the thunk lifecycle actions and the EntityAdapter reducers together, as the reducers expect the real contents as simply action.payload. So, we've altered the action definitions so that the fulfilled action has the promise result as its action.payload, the rejected action has the error as action.error, and the thunk arguments are now in action.meta.args for all action types.
In addition, we wanted to add some kind of unique request ID to each set of lifecycle actions, to help tie them together if necessary. We now generate a unique ID per call to the thunk, using nanoid, and include that value as action.meta.requestId in each action dispatched out of that thunk call. It will also be passed in the options object that is the second argument to the promise callback.
Since we don't have any formal documentation yet, here is the signature of the call to the promise payload creator callback:
const result = (await payloadCreator(args, {
dispatch,
getState,
extra,
requestId
} as TA)) as Returned
and here are the internal action definitions:
const fulfilled = createAction(
type + '/fulfilled',
(result: Returned, requestId: string, args: ActionParams) => {
return {
payload: result,
meta: { args, requestId }
}
}
)
const pending = createAction(
type + '/pending',
(requestId: string, args: ActionParams) => {
return {
payload: undefined,
meta: { args, requestId }
}
}
)
const finished = createAction(
type + '/finished',
(requestId: string, args: ActionParams) => {
return {
payload: undefined,
meta: { args, requestId }
}
}
)
const rejected = createAction(
type + '/rejected',
(error: Error, requestId: string, args: ActionParams) => {
return {
payload: undefined,
error,
meta: { args, requestId }
}
}
)
We've been trying to keep our TS types compatible with multiple TS versions, from 3.3 onwards. We've had to do a number of type workarounds to keep 3.3 compatibility, and it's become painful to keep that going.
Other libraries such as Immer have already jumped up to only supporting TS 3.7+.
For now, we're removing TS 3.3 and 3.4 from our CI runs, and will likely stop supporting them in our library types when 1.3.0 is released.
I've put together a small CodeSandbox that demonstrates use of createSlice, createEntityAdapter, and createAsyncThunk working together in a test:
Redux Toolkit v1.3.0-alpha.1 APIs example
See PR #352: Port ngrx/entity and add createAsyncThunk
https://github.com/reduxjs/redux-toolkit/compare/v1.3.0-alpha.0...v1.3.0-alpha.1
This release adds two new APIs: createEntityAdapter to help manage normalized state, and createAsyncThunk to abstract common data fetching behavior.
This release adds two new APIs: createEntityAdapter to help manage normalized state, and createAsyncThunk to abstract common data fetching behavior.
Note: this is an alpha release. These APIs are currently minimally tested, and the implementation details and API signatures may change. We hope that these APIs will be useful enough to officially release in the near future, and encourage users to try them out and give us feedback.
Please comment in issue #76: Create Async Action, issue #333: Consider adding logic for normalized state, and PR #352: Port ngrx/entity and add createAsyncThunk and let us know your thoughts!
This version is available as @reduxjs/toolkit@alpha on NPM, or @reduxjs/toolkit@1.3.0-alpha.0.
createEntityAdapterThe Redux docs have long 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.
For this alpha release, we've specifically ported the @ngrx/entity library 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 EntityAdapter object 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.
Since this is an alpha, we don't have any API documentation yet. Please refer to the @ngrx/entity API docs for createEntityAdapter as a reference.
There is one very important difference between RTK's implementation and the original @ngrx/entity implementation. With @ngrx/entity, methods like addOne(item, state) accept the data argument first and the state second. With RTK, the argument order has been flipped, so that the methods look like addOne(state, item), and the methods can also accept a standard Redux Toolkit PayloadAction containing the data as the second argument. This allows them to be used as Redux case reducers directly, such as passing them in the reducers argument for createSlice.
Note: we've also updated these methods to use Immer internally. They already made immutable updates, but this simplified the implementation details. However, there is currently an issue we're seeing with nested uses of Immer behaving unexpectedly, so be careful when calling them inside a
createSlicecase reducer. Please see https://github.com/immerjs/immer/issues/533 for details.
createAsyncThunkThe Redux docs have also 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 createAsyncThunk API that abstracts this out. It accepts a base action type string and a callback function that returns a Promise as an argument, 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.
This example demonstrates the basic intended usage of both createEntityAdapter and createAsyncThunk. It's incomplete, but hopefully shows enough of the idea to let you get started:
const usersAdapter = createEntityAdapter();
const fetchUsers = createAsyncThunk(
"users/fetch",
() => usersAPI.fetchAll()
);
// `fetchUsers` is now a typical thunk action creator, but also has
// four more action creators attached:
// pending, fulfilled, finished, rejected
// it will automatically dispatch those based on the promise lifecycle
const usersSlice = createSlice({
name: "users",
initialState: usersAdapter.getInitialState({loading: true}),
reducers: {
// createSlice will generate "users/userAdded" action types for these reducers
userAdded: usersAdapter.addOne,
userRemoved: usersAdapter.removeOne,
// etc
},
extraReducers: {
// would also want to handle the loading state cases, probably with a state machine,
// using the lifecycle actions attached to fetchUsers
[fetchUsers.fulfilled](state, action) {
return usersAdapter.upsertMany(state, action.payload.result)
}
}
});
This release tweaks the type definitions to fix an error where meta and error could not be typed when using the prepare notation of createSlice.
This release tweaks the type definitions to fix an error where meta and error could not be typed when using the prepare notation of createSlice.
prepare (@phryneas - #350)https://github.com/reduxjs/redux-toolkit/compare/v1.2.4...v1.2.5
This release tweaks the RTK-specific ThunkMiddleware type definition for better compatibility.
This release tweaks the RTK-specific ThunkMiddleware type definition for better compatibility.
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>>. The null for the extraArgument option 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.
https://github.com/reduxjs/redux-toolkit/compare/v1.2.3...v1.2.4
This release adds an option to the serializability-check middleware to allow ignoring specific state paths.
This release adds an option to the serializability-check middleware to allow ignoring specific state paths.
The redux-immutable-state-invariant middleware has an option for skipping checks for selected slices of state: https://github.com/leoasis/redux-immutable-state-invariant#api . However, our homegrown serializable-state-invariant middleware only allowed determining if a given value is serializable, and skipping specific actions, not skipping slices of state.
We've now added a ignoredPaths option 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.
https://github.com/reduxjs/redux-toolkit/compare/v1.2.2...v1.2.3
This releases fixes our dev UMD build, and improves type inference for dispatch based on provided middleware.
This releases fixes our dev UMD build, and improves type inference for dispatch based on provided middleware.
The Redux core types will modify the type of dispatch based on provided middleware, allowing it to accept parameters other than action objects and return other values. The redux-thunk middleware is an example of this.
RTK's configureStore and getDefaultMiddleware were not correctly picking up the types of the middleware, and were always assuming that redux-thunk was 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.
RTK now re-exports the ThunkAction type from redux-thunk, and the Draft type from immer.
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.
https://github.com/reduxjs/redux-toolkit/compare/v1.2.1...v1.2.2
Fixed a build tool misconfiguration that resulted in 1.2.0 leaving out the TS typings.d.ts declarations file. No other code changes from v1.2.0.
Fixed a build tool misconfiguration that resulted in 1.2.0 leaving out the TS typings.d.ts declarations file. No other code changes from v1.2.0.
https://github.com/reduxjs/redux-toolkit/compare/v1.2.0...v1.2.1
This rolls up several existing minor releases:
This rolls up several existing minor releases:
This version adds a new mergeReadWriteOnly configuration option (default to false) that, when set to true will not generate separate types for read-only and write-only properties.
true in a schema, it will split the type into two types: one with the read only property suffixed as 'Read' and the other without the read only properties, using the same type name as before.query/param now to avoid conflictsThis release rewrites the createAction and createSlice types 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.)
The type definitions for createAction and createSlice were primarily written using the TS type keyword. The TS compiler and inference engine tries to "unwrap" those types, which meant that the inferred type for a variable like const 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 interface keyword instead. Now, that same variable's inferred type would be displayed as:
ActionCreatorWithPayload<number, "test">
This is more informative and easier to read.
Several users had noted that the complexity of the type definitions for createSlice made it impossible to write a higher-order or wrapper function in TypeScript that called createSlice internally ( #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 to createSlice, and documented how to wrap createSlice in the "Usage with TypeScript" docs page. We've also documented all of the types in the codebase.
Thanks to @phryneas for all the hard work on these type improvements!
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.
type to interface (@phryneas - #273)https://github.com/reduxjs/redux-toolkit/compare/v1.1.0...v1.2.0
Option of generating real TS enums instead of string unions Adds the option of generating real TS enums instead of string unions #2854
Added:
oazapfts back to the current upstream versionThis 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.
createReducer accepts 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 createReducer that 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 extraReducers argument of createSlice. It is not necessary for the reducers argument, as the action types are already being defined there.
Error messages for the serialization check middleware were not correctly displaying the value. This has been fixed.
Our documentation site at https://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).
Thanks to @endiliey, @yangshunz, @wgao19, and @ashakkk for all their hard work on the migration!
We now have a new "Usage with TypeScript" docs page that has examples on how to correctly write and type usage of RTK.
https://github.com/reduxjs/redux-toolkit/compare/v1.0.4...v1.1.0
The redux-starter-kit package continues to work as-is as of v1.0.1. However, to encourage migration, we have deprecated all existing RSK versions, and…
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-kit
to:
@reduxjs/toolkit
Read on for details.
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-kit package 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 https://redux-toolkit.js.org.
Update all dependencies and imports from redux-starter-kit to @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-kit package 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.
Nothing published for this version
Your coding agent can read these notes before it upgrades. Set up the MCP server →