在 React Redux 中选择数组时避免重新渲染的正确方法是什么?

new*_*ent 3 typescript reactjs redux react-redux reselect

有没有办法从 Redux 存储中的数组中选择派生数组而不进行虚假渲染?

我的 Redux 存储包含一个对象数组。

state = {items: [{id: 1, keys...}, {id: 2, keys...}, {id: 3, keys...}, ...]}
Run Code Online (Sandbox Code Playgroud)

我编写了一个选择器来返回 id 数组。

const selectIds = (state: MyStateType) => {
  const {items} = state;
  let result = [];
  for (let i = 0; i < items.length; i++) {
    result.push(items[I].id);
  }
  return result;
};
Run Code Online (Sandbox Code Playgroud)

然后,我在组件内部使用 React-Redux 的useSelector钩子调用这个选择器来渲染组件列表。

const MyComponent = () => {
  const ids = useSelector(selectIds);

  return (
    <>
      {ids.map((id) => (
        <IdComponent id={id} key={id} />
      ))}
    </>
  );
};

Run Code Online (Sandbox Code Playgroud)

我发现MyComponent每次调用调度都会渲染它,这会破坏大量数组元素的性能。

我已经向 useSelector 传递了一个相等函数,如下所示:

import {shallowEqual, useSelector } from "react-redux";

const ids = useSelector(selectIds, (a, b) => {
  if (shallowEqual(a, b)) {
    return true;
  }
  if (a.length !== b.length) {
    return false;
  }
  for (let i = 0; i < a.length; i++) {
    if (a[i].id !== b[i].id) {
      return false;
    }
  }
  return true;
});

Run Code Online (Sandbox Code Playgroud)

但调度被调用的次数足够多,因此对于大量数组元素,检查相等性变得昂贵。

reselect我也尝试过使用该库。

const selectItems = (state: MyStateType) => {
  return state.items;
};

const selectIds = createSelector(
  selectItems,
  (items) => {
    let result = [];
    for (let i = 0; i < items.length; i++) {
      result.push(items[i].id);
    }
    return result;
  }
);
Run Code Online (Sandbox Code Playgroud)

然而,每次我通过调度修改一个数组元素的属性时,都会改变导致重新计算state.items的依赖关系。selectItemsselectIds

我想要的是仅在修改selectIdsid 时重新计算。state.items这可能吗?

小智 5

我认为你在这里能做的最好的事情就是结合reselect使用shallowEqual

import { shallowEqual } from "react-redux";

const selectItems = (state: MyStateType) => state.items;

const selectIds = createSelector(
  selectItems,
  (items) => items.map(item => item.id)
);

const MyComponent = () => {
  const ids = useSelector(selectIds, shallowEqual);

  return (
    <>
      {ids.map((id) => (
        <IdComponent id={id} key={id} />
      ))}
    </>
  );
};
Run Code Online (Sandbox Code Playgroud)

笔记

通过上面的代码:

  • 仅当发生更改时,才会重新创建 ids 数组state.items
  • ids仅当 ids 更改时,该变量才会有新的引用。

如果这个解决方案还不够(买不起shallowEqual)你可以看看https://github.com/dai-shi/react-tracked它使用更精确的系统来跟踪状态的哪一部分被使用(使用代理:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy)。