[React] React Hooks(7) - useCallback

최예린·2022년 8월 7일
0

React

목록 보기
7/19

useCallback이란?

  • useMemo처럼 컴포넌트 최적화를 위해 사용한다.

구조

useCallback(() => {
  return value;
}, [item]};
  • 첫번째 인자: 메모이제이션해줄 콜백 함수

  • 두번째 인자: 의존성 배열 dependency array

  • Javascript의 함수

const calculate = (num) => {
  return num + 1
};

자바스크립트에서 함수는 calculate라는 변수에 함수 객체가 할당되는 것이다.

const calculate = useCallback((num) => {
  return num + 1
}, [item]};

컴포넌트가 렌더링 되면 calculate 함수도 다시 할당받게된다.
useCallback 훅으로 감싸주면 다시 렌더링 될 때 calculate에 새로운 함수 객체를 다시 할당 받지않는다.

[예제1] 기본예제

잘못된 코드

import { useEffect, useState, useCallback } from 'react';

function App() {
  const [number, setNumber] = useState(0);
  
  const someFunction = () => {
    console.log(`someFunc: number : ${number}`);
    return;
  };
  
  useEffect(() => {
    console.log('someFunction이 변경되었습니다.');
  }, [someFunction]);
  
  return (
    <div>
    	<input
    		type="number"
    		value={number}
			onChange={(e) => setNumber(e.target.value)}
        />
        <button onClick={someFunction}>Call someFunc</button>
	</div>
  );
}

export default App;

useMemo와 비슷하다.
number state값이 변경되면 App이 다시 렌더링 되고 somFunction에 새롭게 함수 객체가 할당된다. 그렇기 때문에 그 함수 객체의 주소또한 바뀌게되고, someFunction이 바뀌었다고 인식해서 useEffect가 불려진다.

import { useEffect, useState, useCallback } from 'react';

function App() {
  const [number, setNumber] = useState(0);
  
  const someFunction = useCallback(() => {
    console.log(`someFunc: number : ${number}`);
    return;
  }, []);
  
  useEffect(() => {
    console.log('someFunction이 변경되었습니다.');
  }, [someFunction]);
  
  return (
    <div>
    	<input
    		type="number"
    		value={number}
			onChange={(e) => setNumber(e.target.value)}
        />
        <button onClick={someFunction}>Call someFunc</button>
	</div>
  );
}

export default App;

useCallback으로 somFunction을 감싸주고 빈배열을 의존성 배열로 주면 제일 처음 렌더링될 때 함수 객체를 할당한 뒤 그 뒤 렌더링에는 새로 할당하지않는다.


하지만 이렇게되면 문제가 발생한다. number를 5로 바꿔주었음에도 불구하고 someFunction은 처음 메모이제이션된 값을 불러오기때문에 콘솔에는 number:0 으로 출력된다.

올바른 코드

import { useEffect, useState, useCallback } from 'react';

function App() {
  const [number, setNumber] = useState(0);
  const [toggle, setToggle] = useState(true);
  
  const someFunction = useCallback(() => {
    console.log(`someFunc: number : ${number}`);
    return;
  }, [number]);
  
  useEffect(() => {
    console.log('someFunction이 변경되었습니다.');
  }, [someFunction]);
  
  return (
    <div>
    	<input
    		type="number"
    		value={number}
			onChange={(e) => setNumber(e.target.value)}
        />
		<button onClick={() => setToggle(!toggle)}>{toggle.toString()}</button>
		<br />
        <button onClick={someFunction}>Call someFunc</button>
	</div>
  );
}

export default App;

이를 막기위해 의존성 배열로 number를 주게되면 number가 바뀔때마다 새로 메모이제이션하게 되어 바뀐 number값을 콘솔에 제대로 출력하게된다.

더 확실하게 확인하기위해 렌더링을 위한 새로운 state를 추가하고 토글버튼을 만들었다.

  • number state 값 변경 -> 렌더링 -> 메모이제이션 ⭕
  • 토글버튼 클릭 -> toggle state 값 변경 -> 렌더링 -> 메모이제이션 ❌

[예제2] 박스 키우기

import React, { useCallback, useState } from 'react';
import Box from './Box';

function App() {
  const [size, setSizes] = useState(100);
  const [isDark, setIsDark] = useState(false);
  
  const createBoxStyle = useCallback(() => {
    return {
      backgroundColor: 'pink',
      width: `${size}px`,
      height: `${size}px`,
    };
  }, [size]);
  
  return (
    <div
    	style={{
    		background: isDark ? 'black' : 'white',
    	}}
  	>
    	<input
			type="number"
			value={size}
			onChange={(e) => e.target.value)}
        />
		<button onClick={() => setIsDark(!isDark)}>Change Theme</button>
		<Box createBoxStyle={createBoxStyle} />
    </div>
  );
}

export default App;

Box.js

  • input의 숫자를 키우면 size state가 변경되고 createBoxStyle이 다시 초기화되어 Box에 props로 전달된다.

  • 그러면 Box에서는 createBoxStyle 함수를 받아와 리턴값으로 style state를 변경해주고 콘솔에 '박스 키우기'가 찍히게된다.

  • 하지만 박스 크기와 상관없는 isDark state가 변경되어도 App 컴포넌트가 다시 렌더링되면서 동일하게 '박스 키우기'가 콘솔에 찍히는걸 볼수있다.

이를 막기위해 useCallback으로 createBoxStyle을 감싸고 size state가 변경될 때에만 메모이제이션 되도록한다.

profile
경북대학교 글로벌소프트웨어융합전공/미디어아트연계전공

0개의 댓글