繁体   English   中英

使用 useCallback 和 useMemo 在 React 中重新渲染的问题

[英]Problem with re-renders in React with useCallback and useMemo

我有一个相当简单的组件示例 (Hello.js),它呈现三个组件,每个组件都有不同的 id (Speaker.js)。 我有一个从clickFunction传回的 clickFunction。 我认为当只有一个更改时,使用 React.memo 和 React.useCallback 会阻止所有三个重新渲染,但遗憾的是,您可以从 Speaker.js 中的 console.log 看到,单击三个按钮中的任何一个都会导致所有三个渲染。

这是stackblitz上的问题示例:

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

你好.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>
  );
};

扬声器.js

import React from "react";

export default React.memo(({ speaker, clickFunction }) => {
  console.log(`speaker ${speaker.id} ${speaker.name} ${speaker.favorite}`);
  return (
    <button
      onClick={() => {
        clickFunction(speaker.id);
      }}
    >
      {speaker.name} {speaker.id} {speaker.favorite === true ? "true" : "false"}
    </button>
  );
});

因为当您触发clickFunction时,它会更新扬声器,从而导致重新创建此函数,要解决此问题,您需要从clickFunction依赖项中删除speakers并从setState回调中访问它。 这里的解决方案:

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

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>
  );
};

对于扬声器组件:

import React from "react";

export default React.memo(({ speaker, clickFunction }) => {
  return (
    <button
      onClick={() => {
        clickFunction(speaker.id);
      }}
    >
      {speaker.name} {speaker.id} {speaker.favorite === true ? "true" : "false"}
    </button>
  );
});

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

两件事情:

  1. 传递给 useCallback[speakers]依赖项会导致 function 在每次 speakers更改时重新创建,并且由于回调本身调用 setSpeakers ,因此它将在每次渲染时重新创建。

  2. 如果修复 #1,扬声器组件根本不会重新渲染,因为它们接收到相同的speaker道具。 speaker.favorite已更改的事实不会触发重新渲染,因为speaker仍然是相同的 object。 要解决此问题,请单击 function 返回带有favorite翻转的rec副本,而不是仅在现有 object 中切换它:

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) {
        return { ...rec, favorite: !rec.favorite }; // <= return a copy of rec
      }
      return rec;
    });
    setSpeakers(speakersArrayUpdated);
  }, []); // <= remove speakers dependency

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

暂无
暂无

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

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