Skip to content

Result of functional initialState of injected slice is not memoized with rootReducer.selector #4907

Closed
@Mesoptier

Description

@Mesoptier

My use case
I need to select the state of a slice that may not have been injected yet (returning either undefined, the initial state, or the current state). To handle the initial state if it has been injected but no action has been fired yet, I use rootReducer.selector (as per https://redux-toolkit.js.org/api/combineSlices#selector). This does indeed return the result of initialState().

The problem
However, initialState() is called again every time the selector is called, causing a new state object to be created every time. This in turn causes useSelector to log the "Selector unknown returned a different result when called with the same parameters." warning.

Desired behavior
I would expect the result of initialState() to be memoized by the library.

Though perhaps I'm overlooking a case where you would want the initial state to be recreated (maybe when the same slice is mounted in multiple stores?). If so, it would be nice to update the documentation for createSlice#initialState to inform the user they might want to memoize the result themselves?

Reproducible case
See this CodePen for the reproducible case:

import {
  combineSlices,
  createSlice,
  configureStore
} from "https://esm.sh/@reduxjs/toolkit";

// Set up simple reducer & store.
const rootReducer = combineSlices();
const store = configureStore({ reducer: rootReducer });

// Build `selectTest` using rootReducer, instead of using injectedTestSlice/withTestSlice,
// so it can be used outside of the code-split slice module (weird use case, I know).
const selectTest = rootReducer.selector((rootState) => rootState.test);

console.log(selectTest(store.getState())); // undefined, as expected

// Set up test slice, with functional `initialState` and incrementing `counter` to highlight
// initial state being recreated.
let counter = 0;
const testSlice = createSlice({
  name: "test",
  initialState: () => ({
    counter: counter++
  })
});

testSlice.injectInto(rootReducer);

console.log(selectTest(store.getState())); // { counter: 2 }, expected { counter: 0 }
console.log(selectTest(store.getState())); // { counter: 3 }, expected { counter: 0 }

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions