🐕 useCallback을 쓰는 시점
useMemo와 비슷하게도 메모이제이션을 기반으로 한 훅입니다.
캐싱해둔 값을 그대로 사용하는 건데...
useCallback은 콜백함수 그 자체를 메모이제이션 해줍니다.
useMemo가 return 값을 메모이제이션 해주는 것과는 살짝 대비가 되는 느낌
컴포넌트가 다시 렌더링 되더라도 함수가 초기화되는걸 막을 수 있다.
이후에 렌더링이 될 때는 이전에 이미 만들어둔 함수 객체를 계속 유지하면서 사용할 수 있다.
이걸 왜 써야할까요?
1. 함수 컴포넌트는 상태변화시 다시 렌더링된다.
2. 렌더링 될 때 함수 내부 변수는 초기화된다.
3. 따라서 렌더링 전 변수와 렌더링 후 변수는 메모리 주솟값이 다르다.
4. useEffect 등에서 사용하는 의존성 배열은 주솟값이 다른 객체를 다른 객체로 인식하여 렌더링이 계속 일어난다.
5. 따라서 useCallback을 이용해 함수 자체를 Memoization 해주어야 한다.
👻useCallback의 기본적인 사용법
useCallback(function , dependency)
useMemo와 구조자체는 동일합니다.
대신 인자로 들어간 함수의 리턴값을 기억해주는 게 아니라
콜백 함수 그 자체를 기억 한다는 점
🥶 의존성 배열이 어떻게 되냐면..
import React, { useState, useCallback, useEffect, useMemo } from 'react';
import { Fragment } from 'react';
const App = () => {
const [number, setNumber] = useState(0);
const [toggle, setToggle] = useState(true);
const someFunction = () => {
console.log('someFunc :', number);
};
useEffect(() => {
console.log('someFunction이 변경되었습니다.');
}, [someFunction]);
return (
<>
<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>
</>
);
};
export default App;
이렇게 someFunction이 변경될때마다 useEffect가 호출되는 코드를 작성했습니다.
놀랍게도.. someFunction을 건드리지않고 number 스테이트를 변경시켜서 리렌더링이 일어나도
useEffect가 실행되면서 콘솔창에
'someFunction이 변경되었습니다'가 출력되는 것을 확인할 수 있습니다.
앞서 살펴보았듯이 함수도 객체이고 함수가 호출되면 기존 값을 재활용하는게아니라
아예 새로운 주소에 값을 할당해주기 때문에 일어나는 이슈입니다.
let arr = []
let arr2 = []
console.log(arr === arr2) // false
마치 이것과 같은 이유인거죠!!
이걸 해결해주기 위해서 우리는 someFunction을 어딘가에 저장해둘 필요성이 있을 것입니다.
따라서 useCallback을 고려할 수 있습니다.
🌞useCallback을 사용해 문제를 해결하면?
import React, { useState, useCallback, useEffect, useMemo } from 'react';
import { Fragment } from 'react';
const 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 (
<>
<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>
</>
);
};
export default App;
useCallback안에 함수를 감싸줬습니다.
이렇게하면 useCallback으로 인해 우리가 의도한대로
useEffect는 새로고침을 해줄 때나 실행됩니다.
😋useCallback 응용
이렇게 Change Theme가 변경되었을 때
함수는 새로 만들어지지 않게 하고 싶어서 useCallback으로 감싸주는 형태의 코드를 짜겠습니다.
🤢App.js
import React, { useState, useCallback, useEffect, useMemo } from 'react';
import { Fragment } from 'react';
import Box from './Box';
const App = () => {
const [size, setSizes] = useState(100);
const [isDark, setIsDark] = useState(false);
const createBoxStyle = useCallback(() => {
console.log('createBOX 호출');
return {
backgroundColor: 'pink',
width: `${size}px`,
height: `${size}px`,
};
}, [size]);
return (
<>
<div style={{ background: isDark ? 'black' : 'white' }}>
<input
type="number"
value={size}
onChange={(e) => setSizes(e.target.value)}
/>
<button onClick={() => setIsDark(!isDark)}> Change Theme</button>
<Box createBoxStyle={createBoxStyle} />
</div>
</>
);
};
export default App;
App.js는 다음과 같습니다.
Box.js
import React, { useEffect, useState } from 'react';
const Box = ({ createBoxStyle }) => {
const [style, setStyle] = useState({});
useEffect(() => {
console.log('박스키우기');
setStyle(createBoxStyle());
}, [createBoxStyle]);
return <div style={style}></div>;
};
export default Box;
'react' 카테고리의 다른 글
라이프사이클 메서드 폼 미쳤다. (5) | 2023.03.21 |
---|---|
커스텀 훅으로 맛있는 훅 만들기 (4) | 2023.03.19 |
useMemo를 케이크처럼 쉽게 먹는 방법 (0) | 2023.03.13 |
useReducer를 케이크처럼 쉽게 이해하는 법 (1) | 2023.03.13 |
React 프로젝트 시작 전 환경 세팅하기 (0) | 2023.03.01 |