혼자 적어보는 노트

[React] useCallback에 대한 이해 / 함수 재사용 본문

React

[React] useCallback에 대한 이해 / 함수 재사용

jinist 2022. 1. 17. 11:02

이전 포스팅에서 다룬 React.memo와 함께 사용할

useCallback에 대해 적어보도록 하겠다.

함수를 재사용 한다고 하는데 무슨 의미인지 어떤 식으로 사용을 하는 것인지 알아보자

 

 

useCallback을 이해하기 위한 중요 개념은

함수는 일반 객체와 같은 비교 원칙을 따른다는 것이다.

const func1 = () => 1 + 2;
const func2 = () => 1 + 2;

console.log(func1 === func2) // false;
console.log(func1 === func1) // true;
console.log(func2 === func2) // true;

같은 내용의 함수더라도 비교연산 시 false를 반환한다.

함수는 자기 자신 외의 다른 함수들에서는 false를 반환한다.

 

아래에 작은 예시를 만들어보았다.

import React, { useState } from "react";

import UserName from "./components/UserName";
import UserAge from "./components/UserAge";

function App() {
  const data = { id: 1, name: "Jay", age: 29 };
  const [userData, setUserData] = useState(data);

  const increaseAge = () => {
    setUserData({ ...userData, age: userData.age + 1 });
  };
  const addStar = () => {
    setUserData({ ...userData, name: userData.name + "★" });
  };
  return (
    <div>
      <UserName name={userData.name} addStar={addStar} />
      <UserAge age={userData.age} increaseAge={increaseAge} />
    </div>
  );
}

export default App;

부모 컴포넌트인 App에서 UserData를 생성해주고

userData의 age를 1씩 증가시켜주는 함수 increaseAge와

userData의 name에 ★을 추가해주는 함수 addStar를 만들어 주었다.

 

그리고 각각의 내용에 해당하는 값과 콜백 함수를 UserName, UserAge 컴포넌트에 전달해주었다.

 

import React from "react";

const UserName = ({ name, addStar }) => {
  console.log("Name");
  return (
    <div>
      <p>Name : {name}</p>
      <button onClick={addStar}>★추가</button>
    </div>
  );
};

export default React.memo(UserName);
import React from "react";

const UserAge = ({ age, increaseAge }) => {
  console.log("Age");
  return (
    <>
      <p>Age : {age}</p>
      <button onClick={increaseAge}> + </button>
    </>
  );
};

export default React.memo(UserAge);

UserName과 UserAge에는 props로 데이터와 함수를 전달 받았고

React.memo()로 래핑해주었다.

 

하지만 React.memo로 래핑해주었음에도 불구하고

각각의 버튼 클릭 시 두 컴포넌트에 적은 console이 출력된다.

 

변경되는 props가 없는것 같은데 왜 리렌더가 되는 것일까?

그 것은 앞서 말한 함수 동등성에 의해 일어는 것이다.

 

state가 변경됨에 따라 부모 컴포넌트가 리렌더링이 되고

함수형 컴포넌트에서는 리렌더시에 안에 포함되어 있던 함수들이 전부

새로운 함수로 다시 생성되기 때문에 다시 생성된 함수가 새로운 props로 인식하기 때문이다.

 

부모컴포넌트 업데이트 -> 부모컴포넌트에 있는 함수들이 새로운 함수로 변경됨

-> 이전에 자식 컴포넌트에 props로 받은 콜백함수와 새로 생성되어 props로 전달된 콜백함수를 비교

-> 다른함수로 인식하기 때문에 false반환. -> 자식컴포넌트 리렌더링

 

useCallback()사용

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

import UserName from "./components/UserName";
import UserAge from "./components/UserAge";

function App() {
  const data = { id: 1, name: "Jay", age: 29 };
  const [userData, setUserData] = useState(data);

  const increaseAge = useCallback(() => {
    setUserData({ ...userData, age: userData.age + 1 });
  }, [userData.age]);
  
  const addStar = useCallback(() => {
    setUserData({ ...userData, name: userData.name + "★" });
  }, [userData.name]);
  
  return (
    <div>
      <UserName name={userData.name} addStar={addStar} />
      <UserAge age={userData.age} increaseAge={increaseAge} />
    </div>
  );
}

export default App;

자식컴포넌트에 전달할 함수들을 useCallback()로 감싸주고

두 번째 인자로 해당 함수가 의존하고 있는 데이터를 넘겨준다.

그렇게 하면 리렌더가 되어도 의존하고있는 데이터의 값이 변경되지 않는다면

같은 함수 인스턴스를 반환한다.

Comments