繁体   English   中英

React - 如何从正在更改的对象数组中仅重新渲染一个组件

[英]React - How to rerender only one component from an array of objects that is changing

我有一个与 React 渲染有关的简单问题。 这是我解释之前代码沙箱的链接: https://codesandbox.io/s/list-rerendering-y3iust?file=/src/App.js

这是交易,我有一个对象数组存储在一个名为 App 的父组件中。 每个 object 都有一个“选中”字段,我想在单击该对象的相应复选框时进行切换。 我遍历对象数组并在子组件中显示它们。 当我点击一个复选框时,handleChange function 执行并且该列表在父组件中更新,导致 App 与所有子组件一起重新呈现。 我要解决的问题是,我怎样才能让它只重新渲染被点击的子组件而不是全部?

我尝试使用 useCallback 以及对列表 state 的功能更新,但这没有做任何事情,它仍然重新呈现所有子组件,而不是被切换的子组件。 我有一种预感,我正在错误地使用 useCallback,并且正在创建一个全新的 function。 我想解释一下当涉及到 arrays 时,React 如何重新渲染,并将以前的数组与新数组进行比较。 我知道在我的代码中,我通过解构它然后将其放入一个新数组中来提供原始列表的副本,这显然不是对原始列表的引用,因此 React 将副本设置为新的 state:

应用程序.js

import { useCallback, useState } from "react";
import Child from "./Child";
import "./styles.css";

const mockList = [
  { text: "1", id: 1, checked: false },
  { text: "2", id: 2, checked: false },
  { text: "3", id: 3, checked: false },
  { text: "4", id: 4, checked: false },
  { text: "5", id: 5, checked: false }
];

export default function App() {
  const [list, setList] = useState(mockList);

  const handleChange = useCallback((checked, id) => {
    setList((oldList) => {
      for (let i = 0; i < oldList.length; i++) {
        if (oldList[i].id === id) {
          oldList[i].checked = checked;
          break;
        }
      }
      return [...oldList];
    });
  }, []);

  return (
    <div className="App">
      {list.map((item) => (
        <Child
          key={item.id}
          text={item.text}
          checked={item.checked}
          handleChange={(checked) => handleChange(checked, item.id)}
        />
      ))}
    </div>
  );
}

Child.js

const Child = ({ text, checked, handleChange }) => {
  console.log("Child rerender");
  return (
    <div
      style={{
        display: "flex",
        border: "1px solid green",
        justifyContent: "space-around"
      }}
    >
      <p>{text}</p>
      <input
        style={{ width: "20rem" }}
        type="checkbox"
        checked={checked}
        onChange={(e) => handleChange(e.checked)}
      />
    </div>
  );
};

export default Child;

这是你如何优化它,首先你使用useCallback错误,因为每次重新渲染(e) => handleChange(e.checked)都是一个新实例,因此即使我们记下Child它仍然会重新渲染,因为 props 总是新的。
所以我们需要使用回调到调用 handleChange 的handleChange参见我的分叉代码和框

https://codesandbox.io/s/list-rerendering-forked-tlkwgh?file=/src/App.js

React 与您如何操作 arrays 或对象无关。 它只是继续渲染您的应用程序树,并且有一些规则可以决定何时停止重新渲染树中的某个分支。 渲染只是意味着 React 调用组件函数,这些函数本身返回一个子树或节点。 请参阅https://reactjs.org/docs/reconciliation.html

尝试用React.memo()包装Child

const Child = ({ text, checked, handleChange }) => {
  console.log("Child rerender");
  return (
    <div
      style={{
        display: "flex",
        border: "1px solid green",
        justifyContent: "space-around"
      }}
    >
      <p>{text}</p>
      <input
        style={{ width: "20rem" }}
        type="checkbox"
        checked={checked}
        onChange={(e) => handleChange(e.checked)}
      />
    </div>
  );
};

export default React.memo(Child);

这个React.memo()所做的本质上是将先前的道具与下一个道具进行比较,并且仅在任何道具发生更改时重新渲染Child组件,并且如果道具保持不变则不会重新渲染。

作为旁注:请阅读此https://kentcdodds.com/blog/usememo-and-usecallback
TLDR:不建议以代码复杂性为代价进行过度优化。 并且防止“不必要的”渲染可能会有额外的 memory 使用和计算形式的缺点:所以只有在非常确定它有助于解决特定问题时才需要这样做。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM