본문 바로가기
react

[react] useCallback

by dev__log 2024. 7. 17.

리액트에서 리렌더링이 빈번한 것은 좋지 않다. 리렌더링을 줄이는 것이 중요한데 이 작업을 최적화한다고 할 수 있다. 

불필요한 리렌더링이 발생하지 않도록 하는 최적화 방법에 자주 사용되는 것 중 하나는 바로 useCallback을 사용하는 것이다.

이 외에도 컴포넌트를 캐싱하는 memo(React.memo)와 값을 캐싱하는 useMemo가 있다. 

 

useCallback

오늘 알아볼 훅은 useCallback이다.

useCallback은 함수를 캐싱한다고 할 수 있다. 

함수를 메모리에 저장하여 매번 새로 생성되지 않도록 할 수 있고, 의존성 배열을 추가하여 원하는 때에 다시 메모리에 저장하도록 지정할 수 있다.

 

 

아래의 코드는 App 컴포넌트 하위에 Test 컴포넌트와 FileDrop 컴포넌트가 있는 구조이다.

 

App.tsx

resetCount 함수 : count의 상태를 0으로 초기화

resetCount 함수에 useCallback을 사용하여 상위 컴포넌트인 App 컴포넌트가 리렌더링 되어도 props로 전달받은 Test 컴포넌트가 리렌더링 되지 않도록 하였다.

import FileDrop from './pages/FileDrop'
import Test from './pages/Test'
import {useCallback, useState} from 'react'

export default function App() {
  const [count, setCount] = useState(0)
  
  console.log('App 컴포넌트 렌더링 됨~')
  
  const resetCount = useCallback(() => {
    setCount(0)
  }, [count]) //count 값이 변경될 때 다시 메모리에 저장하기 위해서 의존성 배열에 추가

  const onPlusCount = () => {
    setCount(count => count + 1)
  }

  const onMinusCount = () => {
    setCount(count => count - 1)
  }

  return (
    <div>
      <p>카운트는 {count}</p>
      <button onClick={onPlusCount}>+</button>
      <button onClick={onMinusCount}>-</button>
      <Test resetCount={resetCount} />
      <FileDrop />
    </div>
  )
}

 

App 컴포넌트의 count 값이 변경되어 리렌더링 되는 경우에 하위 컴포넌트도 리렌더링 되는 이유

App 컴포넌트가 리렌더링 될 때 App 컴포넌트에 작성된 함수들도 다 재생성된다.

이렇게 되면 props로 resetCount를 전달받는 Test 컴포넌트는 resetCount 함수의 주소값이 이전과 다르게 변경되는 것이기 때문에 props가 변경되었다고 판단하고 리렌더링 된다.

 

 

Test.tsx

상위 컴포넌트인 App으로부터 resetCount 함수를 내려받았다.

또한, 마지막 줄인 Test 컴포넌트를 React.memo로 감싸서 App 컴포넌트의 count state의 값이 변경되어도 리렌더링 되지 않도록 하였다. 

import React from 'react'

type Props = {
  resetCount: () => void
}

const Test = ({resetCount}: Props) => {

  console.log('하위 컴포넌트 렌더링 됨~')
  
  return (
    <div>
      <button onClick={resetCount}>리셋!</button>
    </div>
  )
}

export default React.memo(Test)

 

 

FileDrop.tsx

로그만 작성했다.

export default function FileDrop() {
  console.log('FileDrop 컴포넌트가 렌더링됐어요')
  return <div>FileDrop</div>
}

 

결과 - useCallback, React.memo 적용

 

  • React.momo를 사용하여 Test 컴포넌트의 '하위 컴포넌트가 렌더링 됨~'은 count state의 값이 변경되어도 나오지 않고 최초 렌더링 시에만 로그가 나타남
  • useCallback을 사용하여 리셋 버튼을 눌렀을 때 Test 컴포넌트의 로그가 나타나지 않음.
  • 최적화 기법을 적용하지 않은 FileDrop컴포넌트의 로그와 상위에 해당하는 App 컴포넌트의 로그만 나타나는 것을 확인할 수 있음.

 

최적화 진행 전

3개의 컴포넌트 로그가 전부 나타는 것을 확인할 수 있다.

 

끝으로

프로젝트를 진행하면서 많이 적용해보지 못한 개념이다. 

위의 코드를 작성하면서 로그를 찍어서 확인해 봤는데 확연하게 리렌더링이 줄어드는 걸 볼 수 있었다. 

 

실제 프로젝트라면 더욱 최적화에 효과적일 것 같다. 

다음 프로젝트에는 꼭 사용해 보도록 해야겠다.

'react' 카테고리의 다른 글

[react] PropsWithChildren  (0) 2024.07.11
[react] 클래스형 컴포넌트  (0) 2024.07.11
react-query useQuery / useMutation  (0) 2024.02.19
axios  (0) 2024.02.16
react-router outlet  (0) 2024.02.07