현재 일기 하나를 삭제하게 되면 전체 일기가 리렌더링이 일어나고 있습니다. 일기가 만약 2천개였다면, 이미지, 비디오를 렌더링했다면 메모리 낭비가 굉장히 심했을 것입니다.
import { useRef, useState } from "react";
const DiaryItem = ({
onEdit,
onRemove,
name,
content,
created_date,
hungry,
id,
}) => {
const [isEdit, setIsEdit] = useState(false);
const toggleIsEdit = () => setIsEdit(!isEdit);
const [localContent, setLocalContent] = useState(content);
const localContentInput = useRef();
const handleRemove = () => {
if (window.confirm(`${id}번째 일기를 정말 삭제하시겠습니까?`)) {
onRemove(id);
}
};
const handleQuitEdit = () => {
setIsEdit(false);
setLocalContent(content);
};
const handleEdit = () => {
if (localContent.localContent < 5) {
localContentInput.current.focus();
return;
}
if (window.confirm(`${id}번째 일기를 수정하시겠습니까?`)) {
onEdit(id, localContent);
toggleIsEdit();
}
};
return (
<div className="DiaryItem">
<div className="info">
<span>
이름 : {name} | 배고픔 정도 : {hungry}
</span>
<br />
<span className="date">{new Date(created_date).toLocaleString()}</span>
</div>
<div className="content">
{isEdit ? (
<>
<textarea
ref={localContentInput}
value={localContent}
onChange={(e) => setLocalContent(e.target.value)}
/>
</>
) : (
<>{content}</>
)}
</div>
{isEdit ? (
<>
<button onClick={handleQuitEdit}>수정 취소</button>
<button onClick={handleEdit}>수정 완료</button>
</>
) : (
<>
<button onClick={handleRemove}>삭제</button>
<button onClick={toggleIsEdit}>수정</button>
</>
)}
</div>
);
};
export default DiaryItem;
DiaryItem.js가 props로 받고 있는 것은 총 7개가 있습니다.
onEdit, onRemove, content에 집중하여 최적화를 해보도록 하겠습니다. 최적화의 첫번째는 React.memo로 컴포넌트를 묶어주는 것 입니다.
import React, { useRef, useState } from "react";
// ... 생략
export default React.memo(DiaryItem);
그 다음 useEffact를 사용하여 어떤 item들이 리렌더링이 일어나는지 확인해보도록 하겠습니다.
const DiaryItem = ({
onEdit,
onRemove,
name,
content,
created_date,
hungry,
id,
}) => {
useEffect(() => {
console.log(`${id}번째 아이템 렌더`);
});
페이지 새로고침, 게시글 하나 삭제를 했을 때 콘솔 로그입니다. 모든 아이템이 리렌더링 되는 것을 확인할 수 있습니다.
onCreate 함수처럼 data state가 변경되면 리렌더링이 일어날 수 밖에 없습니다. App.js의 onEdit과 onRemove를 최적화 해보도록 하겠습니다.
const onRemove = useCallback((targetId) => {
setData((data) => data.filter((it) => it.id !== targetId));
}, []);
const onEdit = useCallback((targetId, newContent) => {
setData((data) =>
data.map((it) =>
it.id === targetId ? { ...it, content: newContent } : it
)
);
}, []);
useCallback을 사용합니다. dependencyArray는 빈 배열을 전달하고, setData에는 함수형으로 전달해줍니다.
이제 일기를 삭제해도 리렌더링이 일어나지 않는 것을 확인할 수 있습니다.
프로젝트의 모든 부분의 렌더링 최적화를 완료하였습니다.
리액트 공식 홈페이지
https://ko.legacy.reactjs.org/docs/react-api.html#reactmemo
해당 게시글은 인프런 강의
"한입 크기로 잘라 먹는 리액트(React.js) : 기초부터 실전까지(이정환)"
를 정리한 내용입니다. 쉽게 잘 설명해주시니 여러분도 강의를 듣는 것을 추천드립니다.