Releases: reduxjs/redux-toolkit
v1.3.0-alpha.1
This release makes several noticeable changes to the alpha createEntityAdapter
and createAsyncThunk
APIs that were introduced in v1.3.0-alpha.0.
Changes
createEntityAdapter
We made several changes to the type definitions for EntityAdapter
and its related types:
- Replaced separate overloads for handling
string
andnumber
IDs with a singletype EntityId = string | number
type, and used that everywhere - Added
EntityAdapter
method type overloads for correct inference ofPayloadAction<T>
when passed directly as a case reducer inside ofcreateSlice
'sreducers
field - Removed the
removeMany(Predicate)
overload, as we discourage passing functions inside of actions
createAsyncThunk
Type Changes
The 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.
Action Payload Changes
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 }
}
}
)
Removal of TS 3.3 and 3.4 from CI
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.
Example
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
Changes
v1.2.5
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
.
Changelog
v.1.3.0-alpha.0
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
.
Changes
createEntityAdapter
The 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
createSlice
case reducer. Please see immerjs/immer#533 for details.
createAsyncThunk
The 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.
Example Usage
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)
}
}
});
v1.2.4
This release tweaks the RTK-specific ThunkMiddleware
type definition for better compatibility.
Changes
Thunk Middleware Type Definition
In v1.2.2, we improved our type definitions to correctly handle whether or not the thunk middleware was being used.
However, the tutorials and documentation recommended using a type like type AppThunk = ThunkAction<void, RootState, null, Action<string>>
. 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.
Changelog
v1.2.3
This release adds an option to the serializability-check middleware to allow ignoring specific state paths.
Changes
Allow Skipping Serializability Checks for State Paths
The redux-immutable-state-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.
Changelog
- Add ignoredPaths option to ignore serializability check (@kevin940726 - #320)
v1.2.2
This releases fixes our dev UMD build, and improves type inference for dispatch
based on provided middleware.
Changes
Dispatch and Middleware Typings
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.
Additional Type Re-Exports
RTK now re-exports the ThunkAction
type from redux-thunk
, and the Draft
type from immer
.
Dev UMD fixes
Our dev UMD build was broken due to the recent build configuration changes, and that has now been fixed. This means the sandbox in the Basic Tutorial should be working again.
Changelog
v1.2.1
v1.2.0
This 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.)
Changes
Type Inference Readability Improvements
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.
Type Export Improvements
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!
Module Bundling Fixes
The build tooling setup for RTK tries to deal with several different use cases (dev vs prod, CJS vs ESM vs UMD modules, etc). There were problems with the build config that resulted in a require()
statement being included in the ESM build, and the UMD dev build was actually missing the immutable invariant middleware. The build tooling has been updated to fix those issues.
Changelog
- exportable case reducers type, bugfix, documentation (@phryneas - #290)
- refactor actionCreator typings from
type
tointerface
(@phryneas - #273) - Mock the console manually (@kevin940726 - #281)
- Use console-testing-library to get consistent snapshots (@kevin940726 - #277)
- Fix module bundling & ESM runtime error (@alex-ketch - #280)
v1.1.0
This release adds a utility function for better type safety with reducer object parameters, and fixes an issue with error message in the serializability check middleware.
Changes
Type-Safe Reducer Object Builder API
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.
Serialization Error Fixes
Error messages for the serialization check middleware were not correctly displaying the value. This has been fixed.
Docs Updates
Docusaurus v2
Our documentation site at 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!
New "Usage with TypeScript" Page
We now have a new "Usage with TypeScript" docs page that has examples on how to correctly write and type usage of RTK.
Changelog
Code
- alternative callback-builder-style notation for actionsMap (@phryneas - #262)
- Inline values in error messages (@kevin940726 - #257)
Docs
v1.0.4: Redux Starter Kit is now Redux Toolkit!
As of v1.0.4, we've officially renamed this package from "Redux Starter Kit" to Redux Toolkit!
Please switch the installed package name from:
redux-starter-kit
to:
@reduxjs/toolkit
Read on for details.
Why A New Name?
The name "Redux Starter Kit" originated from the original discussion issue that started the project, which was titled "RFC: Redux Starter Kit". We originally published it as a personal scoped package during early development (@acemarke/redux-starter-kit
) for ease of management.
The plan was always to make it an official Redux-branded package. We had an extensive discussion of possible names. The redux-starter-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.
Migration
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
.