首页 > 解决方案 > 在 React 中,当用于渲染的数组发生变化时,如何避免不必要的重新渲染?

问题描述

类似的问题中,解决方案是在遍历数组以呈现组件时使用唯一值而不是地图索引作为“键”道具。但是,它对我不起作用,因为在我从数组中删除一个元素后,remaning 组件仍然会重新呈现。我在这里想念什么?

这是我的尝试,但没有按预期工作:</p>

应用程序.js

import { useState } from "react";
import Column from "./Column.js";

export default function App() {
  const data = [
    {
      name: "lebron",
      age: 36,
      team: "lakers"
    },
    {
      name: "chris",
      age: 36,
      team: "suns"
    },
    {
      name: "james",
      age: 32,
      team: "nets"
    }
  ];
  const [players, setPlayers] = useState(data);
  const handleClick = () => {
    return function () {
      const p = [...players];
      p.splice(0, 1);
      setPlayers(p);
    };
  };
  return (
    <>
      <table>
        <tr>
          <td>name</td>
          <td>age</td>
          <td>team</td>
        </tr>
        {players.map((p) => (
          <Column key={p.name} player={p} onClick={handleClick(p.name)} />
        ))}
      </table>
      <button onClick={handleClick()}>Delete</button>
    </>
  );
}

列.js:

import React from "react";

export default function Column({ player, onClick }) {
  console.log(player.name, "update", Date());
  return (
      <tr>
        <td>{player.name}</td>
        <td>{player.age}</td>
        <td>{player.team}</td>
      </tr>
  );
}

标签: javascriptreactjs

解决方案


这是默认的 React 行为。

孩子更新,因为发生了父亲状态的更新。

如果你只想在 props 改变时重新渲染子组件,你需要用React.memo HOC 包装你的子组件。

简而言之,使用React.memoReact 第一次渲染您的组件,记住结果,然后重用上次渲染的结果(跳过不必要的重新渲染)。

React.memo 仅检查 prop 更改。如果你封装的函数组件在其实现中React.memo有一个useState,useReduceruseContextHook ,它仍然会在状态或上下文改变时重新渲染。

在你的情况下:

列.js 文件

import { memo } from "react";

function Column({ player, onClick }) {
  console.log(`Render player ${player.name}`);
  return (
    <tr>
      <td>{player.name}</td>
      <td>{player.age}</td>
      <td>{player.team}</td>
      <button onClick={() => onClick(player)}>Delete</button>
    </tr>
  );
}

export default memo(Column);

应用程序.js 文件

import { useState, useCallback } from "react";
import Column from "./Column";

const data = [
  {
    name: "lebron",
    age: 36,
    team: "lakers"
  },
  {
    name: "chris",
    age: 36,
    team: "suns"
  },
  {
    name: "james",
    age: 32,
    team: "nets"
  }
];

export default function App() {
  const [players, setPlayers] = useState(data);
  const handleClick = useCallback((deleted) => {
    setPlayers((prevState) =>
      prevState.filter((player) => player.name !== deleted.name)
    );
  }, []);

  return (
    <>
      <table>
        <tr>
          <td>name</td>
          <td>age</td>
          <td>team</td>
        </tr>
        {players.map((p) => (
          <Column key={p.name} player={p} onClick={handleClick} />
        ))}
      </table>
      {/* This button makes no sense */}
      <button onClick={() => handleClick(players[0])}>Delete</button>
    </>
  );
}

代码沙箱

我对您的代码进行了一些更改:

  • handleClick功能不对。当您使用时,splice您正在修改玩家数组,并且在 React 状态下只能使用调度函数(setPlayers )进行修改。在您的情况下,您应该使用filter方法,因为它返回一个新数组。

    const handleClick = (deleted) => {
      setPlayers((prevState) =>
        prevState.filter((player) => player.name !== deleted.name)
      );
    };
    
  • Column用 React.memo HOC包装你的组件。

   export default React.memo(Column);
  • 用useCallback包装你的handleClick函数。这是因为你使用 React.memo 需要记忆你的函数。

    const handleClick = useCallback((deleted) => {
      setPlayers((prevState) =>
        prevState.filter((player) => player.name !== deleted.name)
      );
    }, []);
    

推荐阅读