본문 바로가기
JS/React 강의

[React] 19. useCallback()으로 함수 재생성을 방지하기

by GiraffePark 2024. 1. 3.

안녕하세요. 박기린입니다.

useCallback()에 대해 깊이 있게 이해하기 위해, React.memo()를 먼저 알아보는 것을 추천드립니다.

(React.memo() 강의 : https://arnopark.tistory.com/840)

 

 

 

 

 


useCallback()

React.memo() 설명글에서, 몇 가지 코드를 가져오겠습니다.

// App.js
import React, { useState } from 'react';

import Button from './components/UI/Button/Button';
import DemoOutput from './components/Demo/DemoOutput';
import './App.css';

function App() {
  const [showParagraph, setShowParagraph] = useState(false);

  console.log('APP RUNNING');

  const toggleParagraphHandler = () => {
    setShowParagraph((prevShowParagraph) => !prevShowParagraph);
  };

  return (
    <div className="app">
      <h1>Hi there!</h1>
      <DemoOutput show={false} />
      <Button onClick={toggleParagraphHandler}>Toggle Paragraph!</Button>
    </div>
  );
}

export default App;

 

// Button.js
import React from 'react';

import classes from './Button.module.css';

const Button = (props) => {
  console.log('Button RUNNING');
  return (
    <button
      type={props.type || 'button'}
      className={`${classes.button} ${props.className}`}
      onClick={props.onClick}
      disabled={props.disabled}
    >
      {props.children}
    </button>
  );
};

export default React.memo(Button);

바로 App.js와 Button.js 입니다. DemoOutput.js는 React.memo()를 사용하면 함수의 재실행/재평가가 일어나지 않음을 이전 시간에 확인할 수 있었습니다. 하지만 Button.js는 React.memo()를 사용해도 계속 재실행/재평가가 일어납니다.

그 이유는 Button.js에 전달하는 Prop이 함수(object type value)였기 때문입니다.

 

 

 

일반 자바스크립트는, Object 타입일 경우 아무리 내부가 같더라도 다른 값으로 취급합니다.  

 

 

 

포인터의 개념으로 접근해서, obejct의 포인터 위치가 동일할 때만 '같다'라고 인정을 합니다.

useCallback이 하는 일도 위와 동일합니다. useCallback()으로 지정된 함수를 리액트 내부 저장공간에 따로 저장합니다. 그리고 이 함수가 재실행되어야 하는 순간에 맞춰 함수가 작동하게 끔 합니다.

 

 

 


useCallback() 사용법

// App.js 중
  const toggleParagraphHandler = useCallback(() => {
    setShowParagraph((prevShowParagraph) => !prevShowParagraph);
  }, []);

상위 컴포넌트에서 함수 Props 전달하기 전, 함수를 useCallback()으로 감싸주면 됩니다.

꼭, 부모 컴포넌트에서 미리 useCallback()을 적용시킨 후 전달해야 합니다.

 

 

 

export default React.memo(Button);

이후에, 함수 Props를 전달받은 하위 컴포넌트에서 React.memo()를 적용시켜줍니다.

 

 

 

이제 useCallback()이라는 함수를 좀 더 깊이 알아보겠습니다.

useCallback()은 여러 개의 인수를 받습니다.

더보기

useCallback(() => {

 {함수}

}, [의존성 배열])

 

첫 번째 인수는 useCallback을 이용해서 재실행/재평가를 방지하고 싶은 함수를 넣어줍니다.

여기에 넣은 함수는 리액트 내부 저장공간에 따로 저장됩니다. App.js 컴포넌트가 재실행될 때마다 저장공간에서 이 함수를 불러오도록 해서, 함수가 다시 재실행/재평가되는 것을 방지합니다.

 

두 번째 인수는 useEffect()처럼 의존성 배열(dependencies array)을 받습니다.

실제 useEffect()의 의존성 배열과 동일한 역할을 합니다. useCallback()이 전달받는 모든 것을 의존성 배열에 넣어줍니다. (state, props, context ...)

 

 

(의존성 배열이란 무엇인지 더 깊이 있게 설명한 글 : https://arnopark.tistory.com/846)

 

[React] 20. useEffect와 useCallback에서 의존성 배열을 사용하는 이유 (Dependencies Array 설명)

안녕하세요. 박기린입니다. useEffect와 useCallback에는 의존성배열 (dependencies array)를 인수로 받는 영역이 있습니다. useEffect 설명글 : https://arnopark.tistory.com/770 useCallback 설명글 : https://arnopark.tistory.com

arnopark.tistory.com

 

 

 

 

// App.js 중
  const toggleParagraphHandler = useCallback(() => {
    setShowParagraph((prevShowParagraph) => !prevShowParagraph);
  }, []);

다만, 위 코드의 setShowParagraph(setState) 함수처럼 불변성이 React에서 보장되는 함수는 굳이 안 적어줘도 됩니다. 

-> useEffect 역시 동일하며, 만약 의존성 배열에 기입을 까먹은 요소가 있으면 vscode가 알려줄 것입니다.

 

 

 

 

 

 

useCallback() 사용 전 / 후

useCallback()을 버튼에 적용시키고 나니, 'Button RUNNING' 로그가 콘솔에 더 이상 찍히지 않습니다.

 

 

 

 

 


함수 이외의 obect type value는?

함수는 useCallback()을 이용해서 재평가/재실행을 막을 수 있다고 하면, object나 array는 어떻게 해야 할까요?

useMemo()를 사용하면 됩니다. 이건 다음시간에 알아보겠습니다.

 

 

 

반응형