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

Adr*_*llo 5 javascript arrays dom object reactjs

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

事情是这样的,我有一个对象数组存储在名为 App 的父组件中。每个对象都有一个“选中”字段,我想通过单击该对象各自的复选框来切换该字段。我循环遍历对象数组并将它们分别显示在子组件中。当我单击复选框时,handleChange 函数会执行,并且该列表会在父组件中更新,从而导致应用程序与所有子组件一起重新呈现。我想解决的问题是,如何才能只重新渲染被单击的子组件而不是全部组件?

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

应用程序.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>
  );
}

Run Code Online (Sandbox Code Playgroud)

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;

Run Code Online (Sandbox Code Playgroud)

owe*_*edd 3

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

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