Releases: reduxjs/redux-toolkit
v1.8.0-rc.0
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.
Changelog
New "Listener" Middleware
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 Improvements
Middleware 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.
TypeScript Support Matrix Updates
RTK now requires TS 4.1 or greater to work correctly, and we've dropped 4.0 and earlier from our support matrix.
What's Changed
- Rewrite MiddlewareArray and gDM for better Dispatch inference by @markerikson in #2001
- Change listener middleware API name and signature by @markerikson in #2005
- feat(alm): add cancellation message to TaskAbortError, listenerApi.signal & forkApi.signal. by @FaberVitale in #2023
- [fix][1.8.0-integration][alm]: missing type export by @FaberVitale in #2026
- [chore][1.8.0-integration][alm]: apply alm breaking API changes to counter-example by @FaberVitale in #2025
- fix(alm): prevent zombie listeners caused by forked tasks by @FaberVitale in #2070
- Integrate the listener middleware into the RTK package by @markerikson in #2072
- fix(alm): cancel forkApi.delay and forkApi.pause if listener is cancelled or completed by @markerikson in #2074
Full Changelog: v1.7.2...v1.8.0-rc.0
v1.7.2
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.
Changelog
RTK Query Selector TS Types Fix
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.
Additional Configuration Options
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,
},
}),
});
Other API Improvements
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.
Bug Fixes
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.
What's Changed
- feat(meta): Passes meta to result description functions [#1904] by @bever1337 in #1910
- Fix
addMatcher
typings by @crcarrick in #1895 - Pass baseQueryMeta into calculateProvidedBy by @msutkowski in #1926
- Cleanup polls on unsubscribeQueryResult by @msutkowski in #1933
- Add
extra
toprepareHeaders
, update documentation + tests by @msutkowski in #1922 - fix
reducerPath
for query definitions by @phryneas in #1977 - Update serialize documentation link by @wuweiweiwu in #1983
- Refetch should not happen if no active subscribers by @AlexanderArvidsson in #1974
- Add
ignoreActions
flag to serializable state middleware by @msutkowski in #1984 - RTKQ: configurable
structuralSharing
on endpoints/queries/createApi by @msutkowski in #1954
Full Changelog: v1.7.1...v1.7.2
v1.7.1
This release fixes a types issue with RTK 1.7.0 and TS 4.5, as seen in #1829 .
What's Changed
Full Changelog: v1.7.0...v1.7.1
v1.7.0
This feature release has a wide variety of API improvements:
- 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 increateAsyncThunk
- updates
createSlice/createReducer
to accept a "lazy state initializer" function - updates
createSlice
to avoid potential circular dependency issues by lazy-building its reducer - updates Reselect and Redux-Thunk to the latest versions with much-improved TS support and new selector customization options
- Fixes a number of small code and types issues
npm i @reduxjs/toolkit@latest
yarn add @reduxjs/toolkit@latest
Changelog
RTK Query
RTK Query SSR and Rehydration Support
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.
Sharing Mutation Results Across Components
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.
Data Loading Updates
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.
Data Serialization and Base Query Improvements
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
.
Other RTK Query Improvements
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.
RTK Core
createSlice
Lazy Reducers and Circular Dependencies
For 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
Improvements
The 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()
.
Other RTK Improvements
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.
Dependency Updates
We've updated our deps to the latest versions:
- Reselect 4.1.x: Reselect has brand-new customization capabilities for selectors, including configuring cache sizes > 1 and the ability to run equality checks on selector results. It also now has completely rewritten TS types that do a much better job of inferring arguments and catch previously broken patterns.
- Redux Thunk 2.4.0: The thunk middleware also has improved types, as well as an optional "global override" import to modify the type of
Dispatch
everywhere in the app
We've also lowered RTK's peer dependency on React from ^16.14
to ^16.9
, as we just need hooks to be available.
Other Redux Development Work
The Redux team has also been working on several other updates to the Redux family of libraries.
React-Redux v8.0 Beta
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:
RTK "Action Listener Middleware" Alpha
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.
RTK Query CodeGen
The [RTK Query OpenAPI co...
v1.7.0-rc.0
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
Changelog
RTK Query Fixes
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.
Dependency Updates
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.
Other Fixes
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.
What's Changed
- fetchBaseQuery: return nullon empty body for JSON. Add DevWarnings by @phryneas in #1699
- Add unwrap to QueryActionCreatorResult and update LazyQueryTrigger by @msutkowski in #1701
- Only set originalArgs if they're not undefined by @phryneas in #1711
- Treat null as a valid plain object prototype in isPlainObject() in order to sync the util across reduxjs/* repositories by @Ilyklem in #1734
- export RetryOptions interface from retry.ts by @colemars in #1751
- fix: api.util.resetApiState should reset useQuery hooks by @phryneas in #1735
- fix issue where the global RTK object got overwritten in the UMD files by @Antignote in #1763
- Update dependencies and selector types by @markerikson in #1772
- Fix broken sourcemap output due to bad filename replacement by @markerikson in #1773
Full Changelog: v1.7.0-beta.1...v1.7.0-rc.0
v1.7.0-beta.1
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
Changelog
createSlice
Lazy Reducers and Circular Dependencies
For 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.
Dependency Updates
We've updated our deps to the latest versions:
- Reselect 4.1.x: Reselect has brand-new customization capabilities for selectors, including configuring cache sizes > 1 and the ability to run equality checks on selector results. It also now has completely rewritten TS types that do a much better job of inferring arguments and catch previously broken patterns.
- Redux Thunk 2.4.0: The thunk middleware also has improved types, as well as an optional "global override" import to modify the type of
Dispatch
everywhere in the app
We've also lowered RTK's peer dependency on React from ^16.14
to ^16.9
, as we just need hooks to be available.
What's Changed
- Update Yarn from 2.4 to 3.1 by @markerikson in #1688
- allow for circular references by building reducer lazily on first reducer call by @phryneas in #1686
- Update deps for 1.7 by @markerikson in #1692
Full Changelog: v1.7.0-beta.0...v1.7.0-beta.1
v1.7.0-beta.0
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:
Changelog
RTK Query SSR and Rehydration Support
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.
Sharing Mutation Results Across Components
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.
Data Loading Updates
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.
Data Serialization and Base Query Improvements
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
Improvements
The 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()
.
Other Improvements
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.
Related Libraries
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:
- https://github.com/reduxjs/reselect/releases/tag/v4.1.0
- https://github.com/reduxjs/redux-thunk/releases/tag/v2.4.0
What's Changed
- fix "isLoading briefly flips back to
true
" #1519 by @phryneas in #1520 - feat(createAsyncThunk): async condition by @thorn0 in #1496
- add
arg
totransformResponse
by @phryneas in #1521 - add
currentData
property to hook results. by @phryneas in #1500 - use
useSerializedStableValue
for value comparison by @phryneas in #1533 - fix(useLazyQuery): added docs for preferCache option by @akashshyamdev in #1541
- correctly handle console logs in tests by @phryneas in #1567
- add
reset
method to useMutation hook by @phryneas in #1476 - allow for "shared component results" using the
useMutation
hook by @phryneas in #1477 - 🐛 Fix bug with
useMutation
shared results by @Shrugsy in #1616 - pass the ThunkArg to the idGenerator function by @loursbourg in #1600
- Support a custom paramsSerializer on fetchBaseQuery by @msutkowski in #1594
- SSR & rehydration support, suspense foundations by @phryneas in #1277
- add
endpoint
,type
andforced
toBaseQueryApi
andprepareHeaders
by @phryneas in #1656 - split off signature without
AsyncThunkConfig
for better inference by @phryneas in #1644 - Update createReducer to accept a lazy state init function by @markerikson in #1662
- add
selectInvalidatedBy
by @phryneas in #1665
Full Changelog: v1.6.2...v1.7.0-beta.0
v1.6.2
This release fixes several small issues with RTK Query, as well as a regression in the createAsyncThunk
types and an issue with sourcemap URLs.
Changelog
RTK Query Fixes
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.
Other Fixes
Sourcemap URLs should now be correct, especially for the CommonJS build artifacts.
createAsyncThunk
's types have been updated to correctly infer return values when working with enums.
Lots of assorted docs tweaks and updates!
What's Changed
- Add extra to BaseQueryApi by @ricksanchez in #1378
- fix: point sourceMappingURL to correct sourcemaps in build artifacts by @jawadsh123 in #1459
- fix:
createAsyncThunk
union return values fall back to allowing only single member by @phryneas in #1449 - fix
fetchBaseQuery
for usage withnode-fetch
by @phryneas in #1473 - fix "isLoading briefly flips back to
true
" #1519 by @phryneas in #1520
Full Changelog: v1.6.1...v1.6.2
v1.6.1
This release improves several edge cases in RTK Query behavior and implementation, deprecates a lesser-used API, and reverts an internal compatability change from 1.6.
Changelog
RTK Query Tweaks
We've made several small tweaks to the RTK Query implementation:
fetchBaseQuery
now provides a more meaningful error if the response can't be parsed successfullyfetchBaseQuery
has been tweaked to always readfetch
from the global scope, rather than closing over it at creation time. This improves usage with test tools that mock or overridefetch
at the system level, such as Mirage.- The
skipToken
symbol is now created usingSymbol.for()
, to get a consistent reference - API slices now warn if you try to add more than one reducer with the same
reducerPath
name - An internal hook usage was tweaked to avoid the "don't call
useLayoutEffect
on the server" warning being printed in SSR
Also, 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:
- a property on the promise returned by
dispatch
- part of the thunk action
meta
- return value of the
useMutation
hook
Other Changes
The 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.
Changes
- Error handling of fetchBaseQuery (#1250 - @phryneas)
- Warn on duplicate
reducerPath
(#1252 - @phryneas) - Deprecate
getDefaultMiddleware
export (#1258 - @Shrugsy) - Typing for unbound functions (#1263 - @ajcrites)
- Prevent closing over
fetch
(#1267 - @Shrugsy) - Put
enableES5
back inindex.ts
(#1305 - @komar94) - Use
Symbol.for('skipToken')
(#1317 - @phryneas) - Remove
originalArgs
(#1318 - @phryneas) - Call useIsomorphicLayoutEffect to fix warnings (#1319 - @markerikson)
v1.6.0 : RTK Query!
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
redux
package ending up in a project'snode_modules
folder. If you see this issue, you may need to uninstall and reinstallreact-redux
with the latest version, to help ensure noredux
duplicates are in the package tree.
Changelog
RTK Query Data Caching API
RTK Query is a powerful data fetching and caching tool. It is designed to simplify common cases for loading data in a web application, eliminating the need to hand-write data fetching & caching logic yourself.
RTK Query is an optional addon included in the Redux Toolkit package, and its functionality is built on top of the other APIs in Redux Toolkit.
See the RTK Query usage guides and API reference docs for complete information on how to use RTK Query:
https://redux-toolkit.js.org/rtk-query/overview
Motivation
Web applications normally need to fetch data from a server in order to display it. They also usually need to make updates to that data, send those updates to the server, and keep the cached data on the client in sync with the data on the server. This is made more complicated by the need to implement other behaviors used in today's applications:
- Tracking loading state in order to show UI spinners
- Avoiding duplicate requests for the same data
- Optimistic updates to make the UI feel faster
- Managing cache lifetimes as the user interacts with the UI
The Redux core has always been very minimal - it's up to developers to write all the actual logic. That means that Redux has never included anything built in to help solve these use cases. The Redux docs have taught some common patterns for dispatching actions around the request lifecycle to track loading state and request results, and Redux Toolkit's 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:
- The data fetching and caching logic is built on top of Redux Toolkit's
createSlice
andcreateAsyncThunk
APIs - Because Redux Toolkit is UI-agnostic, RTK Query's functionality can be used with any UI layer
- API endpoints are defined ahead of time, including how to generate query parameters from arguments and transform responses for caching
- RTK Query can also generate React hooks that encapsulate the entire data fetching process, provide
data
andisLoading
fields to components, and manage the lifetime of cached data as components mount and unmount - RTK Query provides "cache entry lifecycle" options that enable use cases like streaming cache updates via websocket messages after fetching the initial data
- We have early working examples of code generation of API slices from OpenAPI and GraphQL schemas
- Finally, RTK Query is completely written in TypeScript, and is designed to provide an excellent TS usage experience
Basic Usage
RTK Query is included within the installation of the core Redux Toolkit package. It is available via either of the two entry points below:
import { createApi } from '@reduxjs/toolkit/query'
/* React-specific entry point that automatically generates
hooks corresponding to the defined endpoints */
import { createApi } from '@reduxjs/toolkit/query/react'
For typical usage with React, start by importing 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
}
Bundle Size
RTK Query adds a fixed one-time amount to your app's bundle size. Since RTK Query builds on top of Redux Toolkit and React-Redux, the added size varies depending on whether you are already using those in your app. The estimated min+gzip bundle sizes are:
- If you are using RTK already: ~9kb for RTK Query and ~2kb for the hooks.
- If you are not using RTK already:
- Without React: 17 kB for RTK+dependencies+RTK Query
- With React: 19kB + React-Redux, which is a peer dependency
Adding additional endpoint definitions should only increase size based on the actual code inside the 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.
Build Tooling Improvements
We've completely replaced our previous TSDX-based build tooling pipeline with a custom build pipeline based on ESBuild and TypeScript. This should have no visible changes behavior-wise for end users - we've kept the same build artifact names and ES syntax levels. However, it does speed up our own build process, which is important now that we're generating many more output files.
The published package now also includes a set of "modern" ESM build artifacts that target ES2017 syntax instead of ES5. These files should be smaller than the current "ESM" artifact, which is uses the ES module format but with ES5-level syntax for backwards compatibility.
Most bundlers should currently pick up the ESM artifact as the default, such as in Create-React-App projects. If you are planning to drop IE11 compatibility, you should be able to modify your bundler config to import the modern artifact instead. Since the modern artifact includes the usual process.env.NODE_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:
Redux Toolkit Build Artifacts
| Filename | Module | Syntax | process.env
| Purpose |
|----------------------------------------|--------|-------...