위의 GIF를 주목해주세요. 저장을 수행했는데, 기존에 있던 일기들이 다 날아가고 새로 저장을 수행한 1개의 일기만 리스트에 올라갔습니다.
이런 렌더링 이슈는 추후 컴포넌트의 양이 방대해질때, 메모리 낭비 이슈로 이어지기 때문에 반드시 최적화 작업이 필요합니다.
useCallback()
?useCallback()
전에 알아야 할 지식인 React.memo()
에 대해서 알아야합니다.
React.memo 공식문서에 따르면,
React.memo는 고차 컴포넌트(Higher Order Component)입니다. 즉, React는 컴포넌트를 렌더링하지 않고 마지막으로 렌더링된 결과를 재사용합니다.
이런 상황에서 React.memo
를 활용할 수 있습니다.
상황: 부모 컴포넌트에서 state가 변하게 되면 자식 컴포넌트에 props가 전부 전달되면서, 특정 state만 변했음에도 불구하고 모든 자식 컴포넌트를 렌더링하게 되는 문제가 발생할때
-> 위의 GIF와 같은 상황 의미
useMemo()
?useMemo()
로도 최적화를 수행할 수 있지만 useMemo()
는 콜백함수를 return 할 수 없고, 값 자체를 리턴합니다.
해당 프로젝트에서 일기를 생성/삭제/수정 하는 props는 모두 함수형 컴포넌트의 props로 넘겨지기 때문에 콜백함수 리턴을 지원하는 useCallback()
을 활용하는게 알맞습니다.
실제 useCallback docs에서도 불필요한 렌더링 방지를 위해 활용된다고 설명되어 있습니다.
메모이제이션된 콜백을 반환합니다.
useCallback은 콜백의 메모이제이션된 버전을 반환할 것입니다. 그 메모이제이션된 버전은 콜백의 의존성이 변경되었을 때에만 변경됩니다. 이것은, 불필요한 렌더링을 방지하기 위해 참조의 동일성에 의존적인 최적화된 자식 컴포넌트에 콜백을 전달할 때 유용합니다.
따라서 위의 이슈를 해결하기 위해, 해당 저장을 다루는 함수형 컴포넌트인 DiaryEditor
를 React.memo()의 매개변수로 전달합니다.
export default React.memo(DiaryEditor);
useCallback()
으로 삭제 props 리팩토링 //일기 생성 기능
const onCreate = useCallback(
(author,content,emotion) => {
const created_date = new Date().getTime();
const newItem = {
author,
content,
emotion,
created_date,
id: dataId.current
}
dataId.current += 1;
setData((data)=>[newItem,...data]);
},[]);
여기서 주목해야 될 부분은 setData()
코드 부분입니다.
왜 setData 메서드의 매개변수로 state인 data를 넘겨주게 될까요?
해당 로직은 함수형 업데이트라고 합니다. 함수형 업데이트는 setData 메서드에 인수로 새로운 상태값이 아닌 콜백함수를 전달하는 방식입니다.
이때 setData에 콜백함수로 전달된 함수는 어떤 상황이든 항상 매개변수로 최신의 State를 제공받습니다.
그러므로 함수형 업데이트를 사용하면 useCallback으로 onCreate함수가 재생성되지 않아도 최신의 상태값을 참조할 수 있게 됩니다.
따라서 useCallback에서는 depth의 배열은 빈값으로 할당하여, setState 메서드가 항상 최신의 state를 받아 콜백을 리턴할 수 있도록 작업해야 합니다.
위 과정을 수행 후, 리렌더링 시 정상적으로 저장됌을 확인할 수 있습니다.