yarn @reduxjs/toolkit 1.4.0
v1.4.0

This release updates Immer to v7.x, adds new options for defining "matcher" and "default case" reducers, and adds a new option for adding middleware to the store.

Changes

Immer v7

Immer recently released v7.0.0. The main feature is a new 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.

"Matcher" and "Default Case" Reducers

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.

Middleware Creation

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

Bug Fixes

  • createAsyncThunk could sometimes skip handling a promise under certain conditions, causing an "Uncaught Promise" warning.
  • The updateMany CRUD method of createEntityAdapter wasn't properly merging together multiple changes for the same item ID

Typing Updates

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

Docs Updates

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.

Changes

  • Update docs on serializability usage and dev check middleware (@markerikson - #630)
  • add addMatcher to builder notations & actionMatchers argumen… (@phryneas - #610)
  • Add styling for blockquotes on darkmode (@msutkowski - #615)
  • Add section to usage guide on working with non-serializable data (@cloeper - #623)
  • Fixed multiple updates with same id in updateMany (@jakeboone02 - #621)
  • Bump immer to v7, export current util, add usage docs (@mutkowksi - #604)
  • docs: implement dark mode (@sreetamdas - #575)
  • Export return type of createAsyncThunk (@smrq - #574)
  • Prevent unhandled promises in createAsyncThunk (@msutkowski - #570)
  • Adding correctly typed prepend` and concat` to the array… (@phryneas - #559)
  • add middlewareBuilder notation with type-curried arguments to co… (@phryneas - #549)
  • Allow undefined return type for createAsyncThunk options (@melanieseltzer - #595)

https://github.com/reduxjs/redux-toolkit/compare/v1.3.6...v1.4.0

latest release: 1.5.0
5 months ago