I have a problem with my memoized selectors.
Reading the docs on https://redux.js.org/usage/deriving-data-selectors I taken this snippets:
const state = {
a: {
first: 5
},
b: 10
}
const selectA = state => state.a
const selectB = state => state.b
const selectA1 = createSelector([selectA], a => a.first)
const selectResult = createSelector([selectA1, selectB], (a1, b) => {
console.log('Output selector running')
return a1 + b
})
const result = selectResult(state)
// Log: "Output selector running"
console.log(result)
// 15
const secondResult = selectResult(state)
// No log output
console.log(secondResult)
// 15
My problem is that the secondResult function, log the result.
All this is a little premise.
My very problem:
- I'am using react with @reduxjs/toolkit
- I have a list of todo
- I created a "todoAdapter" to manage a list as entities
- I want to use memoized selected to update a single "todo" without re-render the entire list optimizing my app.
but...
When I dispatch an update with "adapter.updateOne", the standard selector "SelectAll" every time (i think) changes ref.
Example
I have this selectors from "slice"
export const {
selectAll,
selectIds: selectTodosIDs,
selectTotal: selectTodosCount,
selectById: selectTodoById
} = todosSelector;
If I create this selector
export const selectIdsCustom = createSelector(
selectTodosIDs,
(ids) => {
console.log('execute output function');
return ....
}
)
It' all ok (state.todos.ids not change obviously).
If I create this selector:
export const selectTodosCustom = createSelector(
selectAll,
(todos) => {
console.log('execute output function');
return ....
}
)
selectTodosCustom run "always".
Whyyy???
With updateOne I am modifing "only" an entity inside "state.todos.entities"
Where am I wrong ?? What I did not understand?
My app is just un case study. The complete app is on: https://codesandbox.io/s/practical-hermann-60i7i
I only created the same app in typescript and, when I as my app had this problem:
- I download the original app form link above (official redux example)
- npm install
- npm start
But I have the some problem also in the official example!!!!!
Problem is my env? some library version?
Yes, you are right. It is correct.
selectAllfrom@reduxjs/toolkitdepends onidsandentitiesfrom the entities state.Every time you dispatch an update with
adapter.updateOne, the reference to theentitiesobject changes. This is normal, this is how immerjs (used under the hood of reduxtoolkit) provides correct immutability:If the
entitiesobject remained old, the selectorselectAllwould return a memoized value with an incorrect title for the first element.To optimize the re-render of the list (which in actually useful only for large lists), you should use selector
selectIdsin the parent component and selectorselectByIdin the child components.UPDATE
No, you can't do that when using redux. Redux is based on the idea of immutability. Any state change creates a new state object. Updating an entity is just a deep change of the state object. And all objects on the path to the updated element must be new. If you do not follow this rule, then base tools will not work correctly. All of them use strict comparison by default.
But you can still avoid re-render the component even when the selector returns the equal arrays with different references. Just pass your own comparison function: