useCallback(() => {
return value;
}, [item]};
첫번째 인자: 메모이제이션해줄 콜백 함수
두번째 인자: 의존성 배열 dependency array
const calculate = (num) => {
return num + 1
};
자바스크립트에서 함수는 calculate라는 변수에 함수 객체가 할당되는 것이다.
const calculate = useCallback((num) => {
return num + 1
}, [item]};
컴포넌트가 렌더링 되면 calculate 함수도 다시 할당받게된다.
useCallback 훅으로 감싸주면 다시 렌더링 될 때 calculate에 새로운 함수 객체를 다시 할당 받지않는다.
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를 추가하고 토글버튼을 만들었다.
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;
input의 숫자를 키우면 size state가 변경되고 createBoxStyle이 다시 초기화되어 Box에 props로 전달된다.
그러면 Box에서는 createBoxStyle 함수를 받아와 리턴값으로 style state를 변경해주고 콘솔에 '박스 키우기'가 찍히게된다.
하지만 박스 크기와 상관없는 isDark state가 변경되어도 App 컴포넌트가 다시 렌더링되면서 동일하게 '박스 키우기'가 콘솔에 찍히는걸 볼수있다.
이를 막기위해 useCallback으로 createBoxStyle을 감싸고 size state가 변경될 때에만 메모이제이션 되도록한다.