createDraftSafeSelector for use in reducers
Redux Toolkit exports a wrapped version of createSelector named createDraftSafeSelector that allows selectors to work correctly inside createReducer and createSlice reducers with Immer-powered mutable logic. When used with plain state values, it memoizes normally. When used with Immer draft values, it recalculates results instead of using cached values to ensure correctness.
createDraftSafeSelector example showing memoization behavior
Example demonstrating createDraftSafeSelector usage: const selectSelf = (state: State) => state; const unsafeSelector = createSelector(selectSelf, (state) => state.value); const draftSafeSelector = createDraftSafeSelector(selectSelf, (state) => state.value); state.value = 1; const unsafe1 = unsafeSelector(state); const safe1 = draftSafeSelector(state); state.value = 2; const unsafe2 = unsafeSelector(state); const safe2 = draftSafeSelector(state); With Immer draft objects, unsafe1 and unsafe2 return the same cached value, but safe2 returns the updated value of 2 because the safe selector detected the draft object and recalculated.
createDraftSafeSelectorCreator function
Redux Toolkit exports createDraftSafeSelectorCreator, which is the draft-safe equivalent of Reselect's createSelectorCreator. It can be used to create custom selector creators with different memoization strategies. Example: const createWeakMapDraftSafeSelector = createDraftSafeSelectorCreator(weakMapMemoize); const draftSafeSelector = createWeakMapDraftSafeSelector(selectSelf, (state) => state.value);
createDraftSafeSelector.withTypes for pre-typed selectors
As of RTK 2.1, createDraftSafeSelector supports a withTypes method to define a pre-typed version with state type built in. Usage: const createTypedDraftSafeSelector = createDraftSafeSelector.withTypes<RootState>(). This allows setting up the state type once so it does not need to be repeated for each selector call.
createDraftSafeSelector.withTypes limitation with inline arguments
When using createDraftSafeSelector.withTypes, if input selectors are provided as separate inline arguments rather than a single array, the parameter types of the result function will not be inferred. Workarounds include wrapping input selectors in a single array or manually annotating the parameter types of the result function.
entityAdapter.getSelectors returns draft-safe selectors
All selectors created by entityAdapter.getSelectors are draft-safe selectors by default, meaning they can be safely used inside createReducer and createSlice reducers with Immer-powered mutable logic.
Prior to RTK v0.7, createSelector used selectorator
Prior to version 0.7, Redux Toolkit re-exported createSelector from the selectorator library, which allowed using string keypaths as input selectors. This was removed because it did not provide enough benefits and string keypaths made static typing for selectors difficult.
Recommendation against using selectors inside reducers
Selectors typically expect the entire Redux state object as an argument, while slice reducers only have access to a specific subset of the entire Redux state. Additionally, Reselect's createSelector relies on reference comparisons to determine if inputs have changed, and Immer Proxy-wrapped draft values may cause the selector to see the same reference and think nothing has changed, breaking memoization.
createSlice.selectors field support
Redux Toolkit 2.0 adds a selectors field to createSlice that allows defining selectors directly as part of the slice. These are generated assuming the slice is mounted at rootState[slice.name]. You can call slice.getSelectors(selectSliceState) to generate selectors that work with an alternate slice location.
createSelector uses weakMapMemoize as default in Reselect 5.0
In Reselect 5.0, createSelector now uses weakMapMemoize as the default memoizer instead of lruMemoize. This provides an effectively infinite cache size and relies on reference comparisons. If you need custom equality checks, customize createSelector to use lruMemoize with custom memoizeOptions.
createSlice example with selectors
Example of using the selectors field in createSlice:
const slice = createSlice({
name: 'counter',
initialState: 42,
reducers: {},
selectors: {
selectSlice: (state) => state,
selectMultiple: (state, multiplier: number) => state * multiplier,
},
})
const testState = { [slice.name]: slice.getInitialState() }
const { selectSlice, selectMultiple } = slice.selectors
expect(selectSlice(testState)).toBe(slice.getInitialState())
expect(selectMultiple(testState, 2)).toBe(slice.getInitialState() * 2)
createSelector usage within slice selectors
Slice selectors can use createSelector from @reduxjs/toolkit for memoized, derived selectors. Pass input selectors as an array and a combiner function: createSelector([(state: StateType) => state.field], (field) => derived(field)).