react 강의를 듣다가 팀원들 간 useCallback에 관한 얘기가 나와서 관련 예제를 만들어 슬랙에 올렸는데, 블로그에도 올리면 좋을 것 같아서 조금 수정 후에 글을 작성합니다
콜백과 그것의 의존성 값의 배열을 전달하면,
useCallback
은 콜백의 메모이제이션된 버전을 반환합니다. 그 메이오제이션된 버전은 콜백의 의존성이 변경되었을 때만 변경됩니다
이라고 합니다.
데브코스 강의에서는 이보다 쉽게
함수가 다시 정의되는것을 막기 위해 사용하는 훅
으로 설명해주었는데요
react에서 함수가 정의되는것은 무엇이고, 왜 다시 정의되면 안될까요?
react는 가상DOM을 사용해서 달라진 부분만 렌더링합니다
A컴포넌트의 state가 바뀌면, 다른 컴포넌트 말고 A컴포넌트만을 다시 렌더링합니다.
하지만 자식 컴포넌트 역시 다시 렌더링됩니다
부모의 렌더링이 전파된다고 보시면 됩니다.
자식 컴포넌트 입장에서는 props가 바뀌지 않았는데도 불구하고, 부모 컴포넌트가 다시 렌더링되면서 자연스럽게 같이 렌더링됩니다.
불필요한 렌더링을 줄이기 (최적화를) 위해서 react는 메모이제이션 기능을 제공합니다
ex) React.memo, useMemo, useCallback...
위 codepen은 간단하게 만든 useCallback 설명 예제입니다
Test
컴포넌트는 렌더링 되었다는 내용을 console에 남깁니다.Check
컴포넌트는 App의 자식 컴포넌트로 check 유무
, callback함수
를 prop으로 받습니다.print
은 클릭 시 one, two의 체크 유무를 알려줍니다print useCallback
도 클릭 시 one, two의 체크 유무를 알려줍니다빈 배열
이 들어갔습니다print useCallbackTwo
도 클릭 시 one, two의 체크 유무를 알려줍니다one이나 two의 체크 유무를 바꿀 때
console에 Test2는 없지만, Test1은 렌더링되었다는 기록이 남습니다
Test1은 prop으로 받은 callback이 그냥 함수이고,
Test2는 prop으로 받은 callback이 useCallback을 사용한 함수입니다.
이는 one 또는 two state 변경으로 인해 App
컴포넌트가 다시 렌더링되기 때문입니다.
이 때 print() 함수도 다시 정의됩니다.
다시 정의된 print()는 기존의 print()와 같은 로직이지만 다른 함수로 인식됩니다
따라서 props가 바뀌었기 때문에 print()를 prop으로 받는 Test1 컴포넌트는 다시 렌더링됩니다
하지만 useCallback으로 메모이제이션한 printUseCallback()은 다시 정의되지 않습니다
따라서 props가 바뀌지 않았기 때문에 Test2 컴포넌트는 다시 렌더링되지 않습니다
useCallback의 의존성 배열을 주의해야 합니다
one 체크 후 각 버튼 클릭
=> 이 경우 one은 true, two는 false입니다
하지만 printUseCallback()과 printUseCallbackTwo()는 바뀐 one의 값을 제대로 출력하지 못합니다
one 체크, two 체크, one 체크해제 후 각 버튼 클릭
=> 이 경우 one은 false, two는 true입니다
하지만 printUseCallback()은 false, false
printUseCallbackTwo()는 true, true로 출력합니다
useCallback
의 개념 설명에서 메이오제이션된 버전은 콜백의 의존성이 변경되었을 때만 변경됩니다
라고 했습니다
printUseCallback()은 빈 배열이 들어갔기 때문에 컴포넌트가 마운트되고 딱 한번만 정의됩니다. 정의되었을 때의 one, two의 값은 모두 false입니다
printUseCallbackTwo()는 two state가 의존성 배열에 들어갔기 때문에 해당 값이 바뀔 때 정의됩니다.
two가 바뀔 때 one, two의 값은 모두 true입니다.
의존성 배열에 들어가는 값이 바뀔 때 함수를 정의하기 때문에 이를 주의해야 합니다
정리
react에서 함수가 정의되는것은 무엇이고, 왜 다시 정의되면 안될까요?
함수가 정의되는 것
제가 이해하는 useCallback으로 인해 함수가 정의된다는 것은 스코프처럼 당시의 환경을 기억하는 것으로 이해했습니다
정의되는 시점에 state들을 기억하게 됩니다
함수 역시 prop으로 사용할 때는 다시 정의될 경우 그 prop이 다시 렌더링되게 됩니다. 불필요한 렌더링을 줄이는 최적화를 위해서는 함수 역시 필요할 때만 정의되어야 하기 때문에 useCallback
을 사용해야 합니다.
의존성 배열을 주의해야 합니다
잘못된 의존성 배열을 사용할 경우 state가 바뀐것을 처리하지 못할 수 있습니다
함수가 반드시 한번만 정의되어야 하는것은 아닙니다
바뀐 state에 제대로 접근하기 위해서는 함수가 다시 정의되어야 합니다
참고