簡體   English   中英

重新選擇:createSelector 無法正常工作

[英]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,記錄結果。

這一切都是一個小前提。

我的問題:

  • 我正在使用與@reduxjs/toolkit 的反應
  • 我有一份待辦事項清單
  • 我創建了一個“todoAdapter”來管理一個列表作為實體
  • 我想使用 memoized selected 來更新單個“待辦事項”,而無需重新呈現優化我的應用程序的整個列表。

但...

當我使用“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 中創建了相同的應用程序,當我作為我的應用程序遇到這個問題時:

  • 我下載上面的原始應用程序表格鏈接(官方redux示例)
  • npm 安裝
  • npm 開始

但是我在官方例子中也有一些問題!!!!!

問題是我的環境? 一些圖書館版本?

當我使用“adapter.updateOne”發送更新時,標准選擇器“SelectAll”每次(我認為)都會更改 ref。

是的你是對的。 它是正確的。 @reduxjs/toolkit中的selectAll依賴於實體 state 中的idsentities

{
  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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM