在带有useCallback和useMemo的React中重新渲染的问题

时间:2020-07-31 22:15:01

标签: reactjs react-hooks

我有一个相当简单的组件示例(Hello.js),该组件呈现三个组件,每个组件都有不同的ID(Speaker.js)。我有一个select timestamp, sum(case when charge = 'Yearly' then charge else 0 end) yearly, sum(case when charge = 'Monthly' then charge else 0 end) monthly from charges group by timestamp ,我从Speaker.js返回。我认为使用React.memo和React.useCallback会在只有一个更改时阻止所有三个重新渲染,但是可悲的是,您可以从Speaker.js的console.log中看到,单击三个按钮中的任何一个都会导致这三个渲染。

这是stackblitz上的问题示例:

https://stackblitz.com/edit/react-dmclqm

Hello.js

clickFunction

Speaker.js

import React, { useCallback, useState } from "react";

import Speaker from "./Speaker";

export default () => {
  const speakersArray = [
    { name: "Crockford", id: 101, favorite: true },
    { name: "Gupta", id: 102, favorite: false },
    { name: "Ailes", id: 103, favorite: true },
  ];

  const [speakers, setSpeakers] = useState(speakersArray);

  const clickFunction = useCallback((speakerIdClicked) => {
    var speakersArrayUpdated = speakers.map((rec) => {
      if (rec.id === speakerIdClicked) {
        rec.favorite = !rec.favorite;
      }
      return rec;
    });
    setSpeakers(speakersArrayUpdated);
  },[speakers]);

  return (
    <div>
      {speakers.map((rec) => {
        return (
          <Speaker
            speaker={rec}
            key={rec.id}
            clickFunction={clickFunction}
          ></Speaker>
        );
      })}
    </div>
  );
};

2 个答案:

答案 0 :(得分:2)

因为触发clickFunction会更新导致重新创建此功能的扬声器,要解决此问题,您需要从speakers依赖项中删除clickFunction并从{{1} } 打回来。 解决方案:

从“反应”中导入React,{useCallback,useState,useEffect};

setState

和扬声器组件:

import Speaker from "./Speaker";

export default () => {
  const [speakers, setSpeakers] = useState([
    { name: "Crockford", id: 101, favorite: true },
    { name: "Gupta", id: 102, favorite: false },
    { name: "Ailes", id: 103, favorite: true },
  ]);

  const clickFunction = useCallback((speakerIdClicked) => {
    setSpeakers(currentState=>currentState.map((rec) => {
      if (rec.id === speakerIdClicked) {
        rec.favorite = !rec.favorite;
       return {...rec};
      }
     return rec
    }));
  },[]);
  useEffect(()=>{
    console.log("render")
  })

  return (
    <div>
      {speakers.map((rec) => {
        return (
          <Speaker
            speaker={rec}
            key={rec.id}
            clickFunction={clickFunction}
          ></Speaker>
        );
      })}
    </div>
  );
};

答案 1 :(得分:2)

进一步思考后,我认为我的回答可能并不完全正确:没有[speakers]依赖项将无法按预期工作。

两件事:

  1. 传递给[speakers]的{​​{1}}依赖项导致每次useCallback更改时都会重新创建该函数,并且由于回调本身调用speakers,它将在每个渲染器上重新创建。

  2. 如果您修复了#1,则Speaker组件将根本不会重新渲染,因为它们将收到相同的setSpeakers道具。 speaker发生更改的事实不会触发重新渲染,因为speaker.favorite仍然是同一对象。要解决此问题,请让您的click函数返回speaker翻转后的rec的副本,而不是仅在现有对象中切换它:

favorite