Programing

React with useCallback and useMemo의 재렌더 문제

c10106 2022. 3. 27. 13:30
반응형

React with useCallback and useMemo의 재렌더 문제

나는 각각 다른 id(Speaker.js)를 가진 세 가지 구성요소를 렌더링하는 구성요소(Hello.js)의 꽤 간단한 예를 가지고 있다.나는 a를 가지고 있다.clickFunction내가 의장에게서 돌아갔다는 것을.React.memo와 React.useCallback을 사용하면 단 한 가지만 변경될 때 세 가지 모두 다시 렌더링하는 것을 막을 수 있다고 생각하지만, 안타깝게도 스피커의 콘솔.log에서 볼 수 있으며, 세 가지 버튼 중 하나를 클릭하면 세 가지 버튼이 모두 렌더링된다.

스택블리츠의 문제 예는 다음과 같다.

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이 기능을 다시 생성하기 위해 제거해야 하는 스피커를 업데이트한다.speakers로부터clickFunction종속성 및 액세스setState콜백여기서 해결책:

가져오기 리액션, { 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. [speakers]에게 전해진 의존. useCallback함수가 매번 재생성되도록 함 speakers콜백 자체가 전화하기 때문에 setSpeakers모든 렌더에 재현될 것이다.

  2. #1을 고치면 스피커 구성요소가 같은 것을 받기 때문에 전혀 리렌더되지 않는다.speaker소품. 라는 사실.speaker.favorite변화했다고 해서 재조명이 일어나지는 않는다.speaker여전히 같은 대상이다.이 문제를 해결하려면 클릭 함수에 대한 사본을 반환하도록 하십시오.rec와 함께favorite기존 개체에서 전환하지 않고 플립:

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

참조URL: https://stackoverflow.com/questions/63199752/problem-with-re-renders-in-react-with-usecallback-and-usememo

반응형