도리쓰에러쓰

[React] useCallback()과 useMemo() 사용하기 본문

React/React

[React] useCallback()과 useMemo() 사용하기

강도리 2022. 5. 1. 22:18

1. useCallback()과 useMemo()란?

useCallback()useMemo()메모이제이션(Memoization) 된 값을 반환한다.

차이점이 있다면 useCallback()은 메모이제이션된 함수를 반환하고 useMemo()는 어떠한 타입에 상관 없이 메모이제이션된 값을 반환한다.

💡 메모이제이션(Memoization) : 연산의 결과값을 메모리에 저장해두고 이전 값과 결과가 동일할 때 재사용하는 기법

 

 

📌 useCallback()useMemo()를 알기 전 알고 있어야 할 React가 리렌더링을 하는 조건들이 있다.

1️⃣ 자신의 state가 변경될 때

2️⃣ 부모 Component로부터 전달받은 props가 변경될 때

3️⃣ 부모 Component가 리렌더링될 때 (자식 Component도 리렌더링됨)

 

 

React 프로젝트를 개발하다보면 불필요한 리렌더링을 하는 경우가 많다.

이런 경우를 막기 위해 사용하는 것들은 뭐가 있을까?


2. React.memo()

React.memo()Component를 메모이제이션(Memoization)해준다.

부모 Component로부터 받은 props가 같다면 메모이제이션 해둔 렌더링 결과를 가져온다.

만약 렌더링 결과가 이전과 다르다면, React는 DOM을 업데이트한다.

 

 

🔽 React.memo()는 아래와 같이 사용하면 된다.

import React from 'react';

const Button = props => {
    return <button className="button">CLICK!<button>
};

export default React.memo(Button);

아래 예시를 확인해보자.

<App /> Component

import Button from './Button';
import { useState } from 'react';
import './App.css';

function App() {
  const [num, setNum] = useState(0);
  console.log("APP RUNNING!");
  const btnOnClickHandler = () => setNum(0);

  return (
    <div className="App">
      <div onClick={() => setNum(num + 1)}>{num}</div>
      <Button onClick={btnOnClickHandler} />
    </div>
  );
}

export default App;

 

 

<Button /> Component

import React from 'react';

const Button = props => {
  console.log("BUTTON RUNNING!");
  return <button onClick={props.onClick}>RESET!</button>;
};

export default React.memo(Button);

 

 

실행시켜보면 숫자를 클릭할 때 버튼도 다시 리렌더링되는 것을 console창을 통해 알 수 있다.

 

 

분명 부모 Component로부터 받은 props가 같다면 재렌더링을 막아야하는데 <Button /> Component는 다시 재렌더링되는걸까? JavaScript에서 객체는 참조타입이기 때문에 값이 같더라도 참조하는 주소가 다르면 서로 다른 객체라고 인식한다.

 

 

🔽 값(value) vs 참조(reference) 블로그 글 참고하기 !

 

값(value) vs 참조(reference)

\[자바스크립트 개발자라면 알아야 할 33가지 개념 - 번역

velog.io

 

 

copyObj는 obj의 주소값을 복사했기 때문에 동일한 객체라고 인식하지만,

obj와 { a : 1, b : 2 }는 값만 같고 주소가 다르기 때문에 동일한 객체가 아니라고 인식한다.

 

 

그래서 Component는 재렌더링할 때마다 새로운 함수를 생성하고,

React.memo()부모 Component로부터 받은 props가 변경되었다고 판단하였기 때문에 계속 재렌더링하는 것이다.

 

 

❓ 그럼 React.memo()는 언제 사용하면 좋을까?

1️⃣ 순수 함수 Component일 때

2️⃣ 같은 props로 리렌더링될 때

 

 

useCallback()useMemo()는 여기서 발생하는 불필요한 렌더링을 방지하기 위해 사용한다.

우선 useCallback()에 대해서 알아보자.

 


3. useCallback()

useCallback()은 메모이제이션된 함수를 반환한다.

 

 

🔽 useCallback()는 아래와 같이 사용하면 된다.

const aaHandler = useCallback(() => {
    setAa(a);
}, [a]);

배열[ ] 안에 넣어준 값이 바뀔 때에만 새로운 객체를 생성한다.

React.memo()로 감싸준 자식 Component에게 props로 함수를 넘겨줄 때,

넘겨받는 함수를 useCallback()으로 감싸주면 배열[ ] 안에 넣어준 값이 바뀔 경우를 제외하고

항상 동일한 객체를 넘겨줌으로서 불필요한 재렌더링을 막을 수 있다.

 

 

아까 봤던 예시를 아래와 같이 수정해봤다.

function App() {
    ...
    const btnOnClickHandler = useCallback(() => {
        setNum(0);
    }, []);
    ...
}

<Button /> Component로 넘겨주는 함수인 btnOnClickHandleruseCallback()으로 감싸주었더니,

<Button /> Component가 재렌더링되지 않았다.

 

 

<Button /> Component가 재렌더링되지 않는 것을 console창을 통해 알 수 있다.


4. useMemo()

useMemo()는 어떠한 타입에 상관 없이 메모이제이션된 값을 반환한다.

 

 

🔽 useMemo()useCallback()와 동일하게 사용하면 된다.

const aaHandler = useMemo(() => {
    setAa(a);
}, [a]);

 

 

이번엔 useMemo()를 사용하여 <Button /> Component의 재렌더링을 막았다.

function App() {
    ...
    const btnOnClickHandler = useMemo(() => {
        setNum(0);
    }, []);
    ...
}

 

 

<Button /> Component가 재렌더링되지 않는 것을 console창을 통해 알 수 있다.

Comments