useCallback에 관한 이야기
특정함수를 재사용하기 위해 useCallback을 사용한다는데 사실 함수를 정의해서 사용하면 되지 왜? useCallback을 쓰는지 의문이 들어 찾아보게된 배경을 가지고 글을 적어봅니다.
useCallback은 무엇인가?
useCallback
is a React Hook
that lets you cache a function definition between re-renders.
-공식문서 발췌
간단한 코드와 함께 시작하면
App -(theme,id)-> Info -(submitHandler)-> Form 의 형태고,
괄호의 값을 props로 전달한다.
아래의 코드를 참조해서 설명하면
App에서 theme을 바꿀때마다 Form이 다시 리렌더링 된다.
theme props의 영향을 받지않는 Form의 리렌더링을 막기 위해 memo를 이용해봤지만 그래도 리랜더링이 된다.
이유가 뭘까?
Form에 props으로 전달되는 submitHandler가 매번 같은 함수가 아니기 때문이다.
함수의 동작은 같지만 렌더링 될 때 매번 다른 함수를 생성하여 보내기에 props가 달라져 리렌더링이 발생한다.
아래의 코드는 매우 간단하기에 큰 문제가 없지만
만약.. Form 컴포넌트에서 하는일이 많다면! 매우 골치거리가 될 것 같다.
그럼 위 문제에 가장 문제점은 "같은 동작을 하지만 다른 함수가 생성"일 것 같다.
같은 함수임을 알려면 전달된 함수가 변동이 없다면, 같은 값임을 기억해야한다.
useCallback의 정의를 다시 생각해보면
"lets you cache a function definition between re-renders."
정의에 대한 나의 의견은 캐시에 함수를 기억하게 한다면 전달된 props의 값이 같기에 렌더링을 하지 않는다라고 생각한다.
그렇다면,
useCallback은 변경되지 않은 상태(함수)를 기억해 props 변경 여부를 판단하고 리렌더링을 막는 효과를 준다.
라는 결론과 더불어 이걸 useCallback의 장점이라고 말할 수 있을 것 같다.
한가지 더,
useCallback의 deps
useCallback(fn,[deps])의
함수 안에 사용된 props을 deps(의존값)에 넣지 않으면 해당 값이 최신임을 보장할 수 없기에 적어야 한다.
//Info.jsx
import { useCallback } from "react";
import { Form } from "./Form";
export const Shop = ({ id, theme }) => {
const submitHandler = useCallback(
(info) => {
//Restfull API가 보통 있겠죠?
console.log("/post", id, info);
},
[id]
);
return (
<div className={theme}>
<h1>Info</h1>
<Form onSubmit={submitHandler} />
</div>
);
};
//Form.jsx
export const Form = ({ onSubmit }) => {
const submitHandler = (e) => {
e.preventDefault();
const formData = new FormData(e.target);
const info = {
...Object.fromEntries(formData)
};
onSubmit(info);
};
return (
<form onSubmit={submitHandler}>
<label>이름
<input type="text" name="name" />
</label>
<label>나이
<input type="number" name="age" />
</label>
<button type="submit">Submit</button>
</form>
);
};
아래의 이미지를 보면 전혀 영향을 받지않음을 알 수 있다.
결론, 불필요한 리렌더링을 막기 위해 useCallback과 memo가 필요하다.
함수가 매번 다르게 판단되는 점을 알게 되었고, useCallback의 필요성도 알게 되었다.