Повторный выбор: createSelector работает некорректно
У меня проблема с запоминаемыми селекторами.
Читая документы на https://redux.js.org/usage/deriving-data-selectors, я взял эти фрагменты:
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
Моя проблема в том, что функция secondResult регистрирует результат.
Все это небольшая предпосылка.
Сама моя проблема:
- Я использую реакцию с @reduxjs / toolkit
- У меня есть список дел
- Я создал "todoAdapter" для управления списком как объектами.
- Я хочу использовать memoized selected для обновления одного «todo» без повторного рендеринга всего списка для оптимизации моего приложения.
но...
Когда я отправляю обновление с помощью «adapter.updateOne», стандартный селектор «SelectAll» каждый раз (я думаю) меняет ref.
Пример
У меня есть селекторы из "ломтика"
export const {
selectAll,
selectIds: selectTodosIDs,
selectTotal: selectTodosCount,
selectById: selectTodoById
} = todosSelector;
Если я создам этот селектор
export const selectIdsCustom = createSelector(
selectTodosIDs,
(ids) => {
console.log('execute output function');
return ....
}
)
Все в порядке (state.todos.ids, очевидно, не меняется).
Если я создам этот селектор:
export const selectTodosCustom = createSelector(
selectAll,
(todos) => {
console.log('execute output function');
return ....
}
)
selectTodosCustom запускать «всегда».
Почему ???
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?
Help me plese!!
2 ответа
Когда я отправляю обновление с помощью «adapter.updateOne», стандартный селектор «SelectAll» каждый раз (я думаю) меняет ref.
Да ты прав. Это правильно. из
@reduxjs/toolkit
зависит от
ids
и от государства сущностей.
{
ids: ['1', '2'],
entities: {
1: {
id: '1',
title: 'First',
},
2: {
id: '2',
title: 'Second',
},
},
}
Каждый раз, когда вы отправляете обновление с
adapter.updateOne
, ссылка на объект изменится. Это нормально, вот как immerjs (используемый под капотом reduxtoolkit) обеспечивает правильную неизменяемость:
dispatch(updateOne({ id: '1', changes: { title: 'First (altered)' } }));
const state = {
ids: ['1', '2'],
entities: { // <--- new object
1: { // <--- new object
id: '1',
title: 'First (altered)',
},
2: { // <--- old object
id: '2',
title: 'Second',
},
},
};
Если
entities
объект остался старым, селектор
selectAll
вернет мемоизированное значение с неправильным заголовком для первого элемента.
Чтобы оптимизировать повторную визуализацию списка (что на самом деле полезно только для больших списков), вы должны использовать селектор
selectIds
в родительском компоненте и селекторе
selectById
в дочерних компонентах.
const Child = ({ id }) => {
const dispatch = useDispatch();
const book = useSelector((state) => selectById(state, id));
const handleChange = useCallback(() => {
// the parent component will not be re-render
dispatch(
bookUpdate({
id,
changes: {
title: `${book.title} (altered)`,
},
})
);
}, [dispatch, id, book]);
return (
<div>
<h2>{book.title}</h2>
<button onClick={handleChange}>change title</button>
</div>
);
};
function Parent() {
const ids = useSelector(selectIds);
return (
<div>
{ids.map((id) => (
<Child key={id} id={id}></Child>
))}
</div>
);
}
Большое спасибо Denwakeup.
Если вы поищете приложение по ссылке, вы увидите, что там есть фильтры.
Я не могу использовать selectIds, но получаю отфильтрованные идентификаторы из другого селектора на основе «selectAll» и «selectFilters». Мое желание:
- Если я добавляю или удаляю элемент ... да, выберите Все изменить исх. Все хорошо
- но если я изменяю одну сущность, зачем менять ссылки сущностей ?? Я меняю внутреннюю сущность, а не весь объект сущности.
Возможно ли изменение только одного объекта ??
Это похоже на то, что у меня есть:
const item = {
name: 'X'
}
item.name = 'Y'
В этом случае элемент не меняет ссылку, а в «сущностях» я меняю одну опору внутри него.
Это не правильно???
Спасибо