-2

我有一个包含数千个字符串的数组并传递给一个组件:

主要成分:

const array = ['name1', 'name2', 'name3'];
const [names, setNames] = useState(array);

const onClick = (index) => {
  setNames(names.map((name, i) => {
    if (i === index) {
      return 'name changed';
    }
  }; 
};

return (
  <ul>
    {array.map((name, index) => (
      <li key={index}>
        <ShowName name={name} key={index} onClick={() => onClick(index)} />
      </li>
    )}
  </ul>
);

ShowName 组件:

let a = 0;

export default function ShowName({ name, onClick }) {
  a += 1;
  console.log(a);

  return (
    <button type="button" onClick={onClick}>{name}</button>
  );
}

还有一个按钮可以随机更改名称。但是每当按下按钮时,所有ShowName组件都会重新渲染。我一直在尝试使用 useCallback 和 useMemo,但组件仍在重新渲染 x 次(x 是数组的长度)。

const ShowNameHoc = ({ name }) => {
  return <ShowName name={name} />
};

return (
  <div>
    {array.map((name, index) => <ShowNameHoc name={name} key={index} />)}
  </div>
);

如果我只想通过更改重新渲染组件,该怎么办?

4

2 回答 2

2

您对这里的概念有误解。默认情况下,React 会在所有子节点上调用 render,无论 props 是否更改。
在那之后,React 会将新的 Virtual DOM 与当前的 Virtual DOM 进行比较,然后只更新真实 DOM 中发生变化的那些部分。这就是为什么渲染方法中的代码应该可以快速执行的原因。

可以使用或useMemo等功能更改此行为。PureComponentsshouldComponentUpdate

参考资料:
https ://reactjs.org/docs/rendering-elements.html (底部):

即使我们在每个刻度上创建一个描述整个 UI 树的元素,也只有内容发生变化的文本节点会被 React DOM 更新。

https://reactjs.org/docs/optimizing-performance.html#avoid-reconciliation

即使 React 只更新更改的 DOM 节点,重新渲染仍然需要一些时间。在许多情况下,这不是问题,但如果减速很明显,您可以通过覆盖生命周期函数 shouldComponentUpdate 来加快所有速度,该函数在重新渲染过程开始之前触发。
...
在大多数情况下,您可以从 React.PureComponent 继承,而不是手动编写 shouldComponentUpdate()。它相当于实现 shouldComponentUpdate() 对当前和以前的 props 和状态进行浅比较。

此外,请阅读本文了解更多背景信息:https ://dev.to/teo_garcia/understanding-rendering-in-react-i5i


更多细节:
React 中广义上的渲染意味着(简化):

  1. 在可行的情况下使用新的道具更新现有的组件实例(这是列表的关键重要的地方)或创建一个新实例。
  2. 调用render/代表组件的函数如果shouldComponentUpdate返回true
  3. 将更改同步到真实 DOM

这为您提供了以下优化可能性:

  1. 确保您重用实例而不是创建新实例,例如在呈现列表时使用正确的键。为什么?新实例总是会导致旧 DOM 节点从真实 DOM 中移除并添加一个新节点。即使不变。重用实例只会在必要时更新真实的 DOM。render请注意:这对您的组件是否被调用没有影响。
  2. 确保您的渲染方法不会做繁重的工作,或者如果有,请记住这些结果
  3. 使用 PureComponents 或在 props 未更改的情况下完全shouldComponentUpdate阻止调用render

回答您的具体问题:
要真正防止您的ShowName组件被渲染到虚拟 DOM 中 - 如果它们的道具发生变化,您需要执行以下更改:

  1. React.memo在您的 ShowName 组件上使用:
function ShowName({ name, onClick }) {
  return (
    <button type="button" onClick={onClick}>{name}</button>
  );
}

export default memo(ShowName);
  1. onClick通过不在父级的每个渲染上传递新的回调来确保道具实际上没有改变。onClick={() => onClick(index)}每次渲染父级时都会创建一个新的匿名函数。
    防止这种情况有点棘手,因为您想将索引传递给这个 onClick 函数。一个简单的解决方案是创建另一个组件,该组件通过参数和索引传递 onClick,然后在内部用于useCallback构造一个稳定的回调。ShowName不过,这只有在渲染您的操作是一项昂贵的操作时才有意义。
于 2022-01-05T20:44:50.933 回答
-1

发生这种情况是因为您没有在组件 上使用key道具。https://reactjs.org/docs/lists-and-keys.html 它可能看起来像这样<ShowName/>

 return (
<div>
    {array.map(name => <ShowName key={name} name={name} />)}
</div>
);
于 2022-01-05T19:27:41.650 回答