Skip to content

Conversation

@renovate
Copy link

@renovate renovate bot commented Mar 1, 2020

This PR contains the following updates:

Package Type Update Change
@reduxjs/toolkit dependencies minor 1.2.3 -> 1.3.6

Release Notes

reduxjs/redux-toolkit

v1.3.6

Compare Source

This release fixes a couple edge cases with Immer usage and reducers, and exposes the typePrefix field from thunks generated by createAsyncThunk.

Changes

Immer Reducer Fixes

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.

Thunk Type Prefix

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.

Changelog

v1.3.5

Compare Source

This release adds the ability to cancel async thunks before execution, improves TS thunk types, and fixes a broken middleware option name change.

Changes

Async Thunk Cancellation

The 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
      }
    }
  }
)
Thunk Typing Improvements

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.

Immutable Middleware Options

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 .

Changelog

v1.3.4

Compare Source

This release updates our internal nanoid implementation, and exports it for general usage.

Changes

Export nanoid

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

Changelog

v1.3.3

Compare Source

This release improves serializability checking in actions, and exports additional types.

Changes

Action Serializability Checks

The serializability check middleware checks the contents of all dispatched actions. When we added 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.

Type Exports

Per request, we've exported ThunkDispatch from Redux Thunk, and the rest of the internal typedefs related to entities.

Changelog

v1.3.2

Compare Source

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.

Changelog

  • Export createImmutableStateInvariantMiddleware, fix typo (#​449) e3c2cf0

v1.3.1

Compare Source

This release adds additional argument types for some createEntityAdapter CRUD methods.

Changes

createEntityAdapter Insertion APIs

createEntityAdapter 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 Docs

We've also added usage guide examples for createEntityAdapter as well.

Changelog

v1.3.0

Compare Source

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.

New APIs

One of the primary goals for Redux Toolkit has always been to simplify common use cases and reduce "boilerplate" by providing APIs that can replace code you were previously writing out by hand.

To that end, v1.3.0 adds two new APIs for the common use cases of async data fetching and managing normalized data in the store.

createAsyncThunk

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

createEntityAdapter

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

Documentation

We've added new API reference and usage guide sections to the Redux Toolkit docs to cover these new APIs:

Bundle Size Improvements and Dependency Updates

Immer 6.0

Immer has always been the largest chunk of code added to your bundle from using RTK. Until now, RTK specifically depended on Immer 4.x, since 5.x added support for handling Maps 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.

Store Configuration Dependencies

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.

Other Changes

Type Inference Improvements

Users reported that it was possible to pass an entity adapter update method as a case reducer even if the slice state type didn't match what the update method expected (#​434 ). We've updated the TS types to prevent that from being possible.

We've also had a number of cases where users had issues with the typings for action payloads depending on whether strictNullChecks: false was set. We've altered our action creator types to improve that behavior.

Dev Check Middleware Timings

The immutability and serializability dev check middleware both do deep checks of state on every dispatch in dev mode. With a large state tree, this can sometimes noticeably slow down the app, and it's not immediately clear that the dev check middleware are responsible for this.

We've updated both middleware to record how much time is spent actually performing the state checks, and they will now log warning messages if the checks take too long to give you a heads-up that you might want to alter the middleware settings or disable them entirely. The delay is configurable, and defaults to 32ms (two UI frames).

In addition, the serializable middleware now ignores meta.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.

TypeScript Support

We've dropped support for TS versions earlier than 3.5. Given that 3.8 is out, this shouldn't be a major problem for users.

Meanwhile, we've also re-exported the TS types from Reselect for convenience.

Example Usage

This example demonstrates the typical intended usage of both 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
}

Thanks

We'd like to thank the many people who contributed and made this release possible:

  • @​jonjaques : created the initial version of createAsyncThunk that we based our implementation on
  • The entire NgRx team, particularly @​brandonroberts and @​MikeRyanDev , for creating @ngrx/entity and allowing us to port it to Redux Toolkit
  • @​phryneas: our resident TS wizard, who made innumerable improvements to the TS behavior and createAsyncThunk implementation
  • @​msutkowski : lots of feedback on the error handling design for createAsyncThunk
  • @​Ethan-Arrowood: pointed us to a novel technique he'd developed for optional overriding of TS generic arguments
  • @​mweststrate: the creator of Immer, who made massive improvements to its tree shaking and bundle size capabilities, published a utility for tracking tree shaking sizes of exports, and kept in touch with us on the status of Immer 6
  • All the users who actually tried out the alphas and betas and gave us feedback on bug reports and API design.

Changelog

For the complete set of code changes, see:

and this diff:

For the iterative changes as this release was developed, see the Releases page for the individual release notes.

v1.2.5

Compare Source

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

v1.2.4

Compare Source

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


Renovate configuration

📅 Schedule: "every weekend" in timezone Asia/Tokyo.

🚦 Automerge: Enabled.

♻️ Rebasing: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

🔕 Ignore: Close this PR and you won't be reminded about this update again.


  • If you want to rebase/retry this PR, check this box

This PR has been generated by WhiteSource Renovate. View repository job log here.

@renovate renovate bot added the renovate label Mar 1, 2020
@renovate renovate bot force-pushed the renovate/reduxjs-toolkit-1.x branch from 9420891 to d3b0532 Compare March 24, 2020 14:07
@renovate renovate bot changed the title Update dependency @reduxjs/toolkit to v1.2.5 Update dependency @reduxjs/toolkit to v1.3.0 Mar 24, 2020
@renovate renovate bot force-pushed the renovate/reduxjs-toolkit-1.x branch from d3b0532 to 06a6ce6 Compare March 27, 2020 01:17
@renovate renovate bot changed the title Update dependency @reduxjs/toolkit to v1.3.0 Update dependency @reduxjs/toolkit to v1.3.1 Mar 27, 2020
@renovate renovate bot force-pushed the renovate/reduxjs-toolkit-1.x branch from 06a6ce6 to 75bfb27 Compare March 28, 2020 01:58
@renovate renovate bot changed the title Update dependency @reduxjs/toolkit to v1.3.1 Update dependency @reduxjs/toolkit to v1.3.2 Mar 28, 2020
@renovate renovate bot force-pushed the renovate/reduxjs-toolkit-1.x branch from 75bfb27 to 086bd8d Compare April 4, 2020 19:34
@renovate renovate bot changed the title Update dependency @reduxjs/toolkit to v1.3.2 Update dependency @reduxjs/toolkit to v1.3.3 Apr 4, 2020
@renovate renovate bot force-pushed the renovate/reduxjs-toolkit-1.x branch from 086bd8d to f0383e6 Compare April 5, 2020 19:17
@renovate renovate bot changed the title Update dependency @reduxjs/toolkit to v1.3.3 Update dependency @reduxjs/toolkit to v1.3.4 Apr 5, 2020
@renovate renovate bot force-pushed the renovate/reduxjs-toolkit-1.x branch from f0383e6 to 2f78fa6 Compare April 19, 2020 19:55
@renovate renovate bot changed the title Update dependency @reduxjs/toolkit to v1.3.4 Update dependency @reduxjs/toolkit to v1.3.5 Apr 19, 2020
@renovate renovate bot force-pushed the renovate/reduxjs-toolkit-1.x branch from 2f78fa6 to a4d7f5e Compare May 15, 2020 22:00
@renovate renovate bot changed the title Update dependency @reduxjs/toolkit to v1.3.5 Update dependency @reduxjs/toolkit to v1.3.6 May 15, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants