[英]Reselect: createSelector not working correctly
我的記憶選擇器有問題。
閱讀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 function,記錄結果。
這一切都是一個小前提。
我的問題:
但...
當我使用“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 ....
}
)
選擇 TodosCustom 運行“始終”。
為什么???
使用 updateOne,我“僅”修改“state.todos.entities”中的一個實體
我哪里錯了?? 我不懂的是?
我的應用程序只是一個案例研究。 完整的app在: https://codesandbox.io/s/practical-hermann-60i7i
我只在 typescript 中創建了相同的應用程序,當我作為我的應用程序遇到這個問題時:
但是我在官方例子中也有一些問題!!!!!
問題是我的環境? 一些圖書館版本?
當我使用“adapter.updateOne”發送更新時,標准選擇器“SelectAll”每次(我認為)都會更改 ref。
是的你是對的。 它是正確的。 @reduxjs/toolkit
中的selectAll
依賴於實體 state 中的ids
和entities
。
{
ids: ['1', '2'],
entities: {
1: {
id: '1',
title: 'First',
},
2: {
id: '2',
title: 'Second',
},
},
}
每次使用adapter.updateOne
發送更新時,對entities
object 的引用都會發生變化。 這是正常的,這就是 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
object 仍然是舊的,選擇器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>
);
}
更新
在這種情況下,項目不會更改 ref 並且在“實體”中我正在更改其中的單個道具。
這是不對的???
不,使用 redux 時不能這樣做。Redux 基於不變性的思想。 任何 state 的變化都會創建一個新的 state object。更新實體只是對 state object 的深度變化。並且更新元素路徑上的所有對象都必須是新的。 如果不遵循此規則,則基本工具將無法正常工作。 默認情況下,它們都使用嚴格比較。
但是,即使選擇器返回具有不同引用的相等 arrays,您仍然可以避免重新渲染組件。 自己對比一下就function:
...
const todoIds = useSelector(
selectFilteredTodoIds,
// the component will be re-render only if this function returns false
(next, prev) => next.every((id, index) => id === prev[index])
);
...
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.