useMemo
를 사용하면 연산을 최적화할 수 있습니다. 두번째 파라미터로 전달된 의존성 값이 변경될때만 전달 된 함수를 실행하게 됩니다.
아래와 같이 코드를 작성 후 input 창에 값을 입력할때마다 콘솔이 찍히는 것을 확인할 수 있습니다.
const getAverage = numbers => {
console.log('평균값 계산중..');
if (number.length === 0) return 0;
const sum = numbers.reduce((a, b) => a + b);
return sum / numbers.length;
}
const Average = () => {
const [number, setNumber] = useState('');
const [list, setList] = useState([]);
const onChange = e => setNumber(e.target.value);
const onInsert = () => {
const nextList = list.concat(parseInt(number));
setList(nextList);
setNumber('');
}
return (
<input value={number} onChange={onChange} />
<button onClick={onInsert}>등록</button>
<ul>
{list.map((value, index) => (
<li key={index}>{value}</li>
))}
</ul>
<div>
<b>평균값:</b>{getAverage}
</div>
)
}
useMemo
를 이용해서 의존 상태값이 변할때만 함수를 호출하도록 변경해보겠습니다.
(...)
const Average = () => {
(...)
const avg = useMemo(() => getAverage(list), [list]);
return (
(...)
<div>
<b>평균값:</b>{avg}
</div>
)
}
이제는 input에 값을 입력하여도 콘솔이 찍히지 않고, list가 변할때만 콘솔이 찍히는 걸 확인할 수 있습니다.
useCallback
은 useMemo
와 같이 메모이제이션 기능을 활용하여, 최적화하는데 사용합니다. useCallback
을 사용하면 의존성 값이 변경될때만 이벤트 핸들러 함수를 생성할 수 있습니다.
const Counter = () => {
const [count, setCount] = useState(0);
const onIncrease = () => setCount(count + 1);
const onDecrease = () => setCount(count - 1);
return (
<div>
<button onClick={onIncrease}>+1</button>
<button onClick={onDecrease}>-1</button>
<p>{count}</p>
</div>
)
}
위와 같은 카운터 컴포넌트가 존재합니다. 현재는 렌더링 될때마다 이벤트 핸들러 함수가 새롭게 생성이 됩니다. 아래와 같이 변경하도록 하겠습니다.
const Counter = () => {
const [count, setCount] = useState(0);
const onIncrease = useCallback(
() => setCount(count + 1),
[count]
);
const onDecrease = useCallback(
() => setCount(count - 1),
[count]
);
return (
<div>
<button onClick={onIncrease}>+1</button>
<button onClick={onDecrease}>-1</button>
<p>{count}</p>
</div>
)
}
useCallback(fn, deps)는 useMemo(() => fn, deps)와 동일합니다.
함수내부에서 의존성 값을 사용한다면 두번째 파라미터의 배열에 추가해주어야 합니다.
그렇지 않으면 함수를 새롭게 생성하지 않기때문에, 현재값이 아닌 함수 생성 당시의 값을 참조하게 될 것입니다.
useRef
로 반환 된 객체를 이용해서 컴포넌트의 ref 속성을 설정할 수 있습니다. 그러면 ref 객체의 .current
속성에 DOM 노드를 설정해 줍니다.
const Input = () => {
const [value, setValue] = useState('');
const [list, setList] = useState([]);
const inputEl = useRef(null);
const onChange = e => setValue(e.target.value);
const onClick = () => {
const nextList = list.concat(value);
setList(nextList);
setValue('');
inputEl.current.focus();
}
return (
<div>
<input value={value} onChange={onChange} />
<button onClick={onClick}>등록</button>
</div>
)
}
useRef를 사용하여 ref를 설장하면 엘리먼트에 직접 접근을 할 수 있습니다. 스크롤이벤트나, focus 등과 같이 엘리먼트에 직접 접근해야 하는 경우에 useRef를 사용할 수 있습니다.
로컬 변수를 사용할 때도 useRef를 사용할 수 있습니다. ref 객체의 생명주기는 컴포넌트의 생명주기와 동일하기 때문에 이를 활용하여 로컬 변수를 사용할 수 있습니다.
const RefSample = () => {
const id = useRef(1);
const setId = (n) => {
id.current = n;
}
const printId = () => {
console.log(id.current);
}
return (
<div>
refsample
</div>
)
}
.current 값은 변경이 되어도 리렌더링 되지 않습니다.
useMemo
, useCallback
와 같은 훅은 최적화를 할 때 사용이 되는 훅입니다. 하지만 잘못 사용하였을 경우 해당 훅들을 사용했을때보다 더 많은 비용을 초래할 경우가 생길 수 있습니다. 대부분의 경우에는 이러한 최적화 훅들을 사용할 필요가 없습니다.