useCallback
2023-09-04
ReactHooks
Table of Contents
useCallback이 나오게 된 배경
- 함수 컴포넌트 내에서 함수를 선언할 때, 매 렌더링마다 새로운 함수 인스턴스가 생성되어, 불필요한 렌더링과 성능 저하를 유발하는 경우가 있었습니다.
1. 프로젝트 세팅 React + TS + Vite
$ npm init vite@latest React-UseCallback -- --template react-ts
$ cd React-UseCallback
$ npm i
useCallback의 구조
const cachedFn = useCallback(fn, dependencies)
- useCallback의 첫 파라미터는 cache할 함수이고, dependencie는 배열객체가 들어갑니다. 의존성 배열안에 할당한 값이 변경될 때마다 다시 생성됩니다.
- useCallback은 Function 타입인 T타입을 상속받고, 첫번째 파라미터인 callback의 타입은 T타입이고, 두번째 파라미터의 deps는 의존성 배열입니다. 그리고 T타입의 함수를 리턴받습니다.
함수 동등성
const add1 = () => x + y;
const add2 = () => x + y;
add1 === add2
> false
- 자바스크립트에서 함수는 객체로 취급이 되기때문에, 함수를 동일하게 만들어도 메모리 주소가 다르면 다른 함수로 간주하고 있습니다. 메모리 주소에 의한 참조 비교가 일어나기 때문입니다.
2. useCallback 사용해보기
/src/App.tsx
import "./App.css";
import { useState } from "react";
import AppChild from "./AppChild";
function App() {
const [state, setState] = useState(false);
function onClickHandler(event: React.MouseEvent) {
event.preventDefault();
setState((prev) => !prev);
}
return (
<>
<button onClick={onClickHandler}>render {`${state}`}</button>
<AppChild />
</>
);
}
export default App;
- 부모 컴포넌트입니다. useState를 사용하여 상태값을 관리하고 있습니다.
/src/AppChild.tsx
import React, { useCallback, useCallback } from "react";
const AppChild = () => {
console.log("AppChild render ::: ");
// 일반 함수
function create() {
for (let i = 0; i < 99999; i++) {
console.log(i);
}
}
useCallback(() => {
console.log("useCallback ::: create");
}, [create]);
return <div>Appchild</div>;
};
export default AppChild;
- useCallback에 useCallback으로 감싼 useCreateCallback 함수와 일반 함수 create를 넣었습니다.
App.tsx에서 버튼을 트리거하면 부모 컴포넌트가 리렌더링 됩니다. 자식도 영향을 받아 리렌더링되고, useCallback에 할당된 일반 변수 create는 다시 재할당되어 메모리 주소에 의한 참조 비교로 같은 함수여도 다른 주소를 할당받아 변경된 함수로 인식하게 됩니다. 그래서 useCallback는 주소값이 다른 변화를 감지하여 console.log를 실행할 것입니다.
useCallback 추가
import React, { useCallback, useCallback } from "react";
const AppChild = () => {
console.log("AppChild render ::: ");
// 일반 함수
function create() {
for (let i = 0; i < 99999; i++) {
console.log(i);
}
}
// 메모이징된 함수, 의존성 배열에 아무것도 넣지 않으면 mount될때 실행
const useCreateCallback = useCallback(create, []);
useCallback(() => {
console.log("useCallback ::: useCreateCallback");
}, [useCreateCallback]);
useCallback(() => {
console.log("useCallback ::: create");
}, [create]);
return <div>Appchild</div>;
};
export default AppChild;
- useCallback을 추가했습니다. 이제 useCallback에서 useCreateCallback과 일반 함수 create의 차이를 확인할 수 있습니다. useCallback는 useCreateCallback을 트리거하지 않고 있습니다.