이제 여기서 Memo를 줫을때와 주지 않았을 때를 비교해보자!
CommentItem.jsx 파일
import React, { Profiler,memo } from 'react'
import './CommentItem.css'
function CommentItem({title,content,likes}) {
function onRenderCallback(
id, // 방금 커밋된 Profiler 트리의 "id"
phase, // "mount" (트리가 방금 마운트가 된 경우) 혹은 "update"(트리가 리렌더링된 경우)
actualDuration, // 커밋된 업데이트를 렌더링하는데 걸린 시간
baseDuration, // 메모이제이션 없이 하위 트리 전체를 렌더링하는데 걸리는 예상시간
startTime, // React가 언제 해당 업데이트를 렌더링하기 시작했는지
commitTime, // React가 해당 업데이트를 언제 커밋했는지
interactions // 이 업데이트에 해당하는 상호작용들의 집합
) {
// 렌더링 타이밍을 집합하거나 로그...
console.log(`actualDuration(${title}: ${actualDuration})`)
}
return (
<Profiler id="CommentItem" onRender={onRenderCallback}>
<div className='CommentItem'>
<span>{title}</span>
<br/>
<span>{content}</span>
<br/>
<span>{likes}</span>
<br/>
</div>
</Profiler>
)
}
export default CommentItem;
Memo를 주지 않게되면 위 이미지 처럼 하나를 생성할 때 하나 만 렌더링되는게 아니라 기존에 생성되어있던 값들을 같이 렌더링한다.
export default memo(CommentItem);
하지만 memo를 넣어줌으로써 하나씩 렌더링되는 것을 확인할 수 있다.
CommentItem.jsx
import React, { Profiler,memo } from 'react'
import './CommentItem.css'
function CommentItem({title,content,likes,onClick}) {
function onRenderCallback(
id, // 방금 커밋된 Profiler 트리의 "id"
phase, // "mount" (트리가 방금 마운트가 된 경우) 혹은 "update"(트리가 리렌더링된 경우)
actualDuration, // 커밋된 업데이트를 렌더링하는데 걸린 시간
baseDuration, // 메모이제이션 없이 하위 트리 전체를 렌더링하는데 걸리는 예상시간
startTime, // React가 언제 해당 업데이트를 렌더링하기 시작했는지
commitTime, // React가 해당 업데이트를 언제 커밋했는지
interactions // 이 업데이트에 해당하는 상호작용들의 집합
) {
// 렌더링 타이밍을 집합하거나 로그...
console.log(`actualDuration(${title}: ${actualDuration})`)
}
const handleClick = () =>{
onClick();
alert(`${title} 눌림`);
}
return (
<Profiler id="CommentItem" onRender={onRenderCallback}>
<div className='CommentItem' onClick={handleClick}>
<span>{title}</span>
<br/>
<span>{content}</span>
<br/>
<span>{likes}</span>
<br/>
</div>
</Profiler>
)
}
export default memo(CommentItem);
comments.jsx
import React from 'react'
import CommentItem from './CommentItem'
export default function Comments({commentList}) {
return (
<div>
{commentList.map(comment => <CommentItem
key={comment.title}
title={comment.title}
content={comment.content}
likes={comment.likes}
onClick={() => console.log('눌림')};
/>)}
</div>
)
}
위 코드로 자식에게 클릭이벤트를 추가했는데 다시 처음과 같은 현상이 일어남.
메모이제이션을 말할 떄 props가 동일한 상태 아이탬에다가 인라인으로 함수를 만들어주면 함수가 렌더링 될 때마다 새로운 함수가 만들어지기 때문에 메모로 해도 똑같은 현상은 일어남.
그렇다면 아래 코드 처럼 인라인이 아닌 함수로 빼서 사용했는데도 똑같은 현상이 일어나게 된다.
그이유는 comments 자체가 리렌더링 되기 때문에 똑같이 일어난다.
Comments.jsx
import React from 'react'
import CommentItem from './CommentItem'
export default function Comments({commentList}) {
const handleClick = () =>{
console.log('눌림');
}
return (
<div>
{commentList.map(comment => <CommentItem
key={comment.title}
title={comment.title}
content={comment.content}
likes={comment.likes}
onClick={handleClick}
/>)}
</div>
)
}
이런 상태가 유지 됬을 때 사용하는 것이 useCallback
이다.
Comments.jsx
import React,{useCallback} from 'react'
import CommentItem from './CommentItem'
export default function Comments({commentList}) {
const handleClick = useCallback(() =>{
console.log('눌림');
},[]);
return (
<div>
{commentList.map(comment => <CommentItem
key={comment.title}
title={comment.title}
content={comment.content}
likes={comment.likes}
onClick={handleClick}
/>)}
</div>
)
}
새로운 요구사항인 likes 10이상이면 good 아니면 bad로 나타내라는 요구사항이 추가되었다고 가정한다.
const rate = () =>{
console.log('rate');
return likes > 10 ? 'Good' : 'Bad'
}
return (
<Profiler id="CommentItem" onRender={onRenderCallback}>
<div className='CommentItem' onClick={handleClick}>
<span>{title}</span>
<br/>
<span>{content}</span>
<br/>
<span>{likes}</span>
<br/>
<span>{rate()}</span>
<br/>
</div>
</Profiler>
)
import React, { Profiler,memo,useState } from 'react'
import './CommentItem.css'
function CommentItem({title,content,likes,onClick}) {
const [clickCount, setClickCount] = useState(0);
function onRenderCallback(
id, // 방금 커밋된 Profiler 트리의 "id"
phase, // "mount" (트리가 방금 마운트가 된 경우) 혹은 "update"(트리가 리렌더링된 경우)
actualDuration, // 커밋된 업데이트를 렌더링하는데 걸린 시간
baseDuration, // 메모이제이션 없이 하위 트리 전체를 렌더링하는데 걸리는 예상시간
startTime, // React가 언제 해당 업데이트를 렌더링하기 시작했는지
commitTime, // React가 해당 업데이트를 언제 커밋했는지
interactions // 이 업데이트에 해당하는 상호작용들의 집합
) {
// 렌더링 타이밍을 집합하거나 로그...
console.log(`actualDuration(${title}: ${actualDuration})`)
}
const handleClick = () =>{
onClick();
setClickCount(prev=>prev+1);
alert(`${title} 눌림`);
}
const rate = () =>{
console.log('rate');
return likes > 10 ? 'Good' : 'Bad'
}
return (
<Profiler id="CommentItem" onRender={onRenderCallback}>
<div className='CommentItem' onClick={handleClick}>
<span>{title}</span>
<br/>
<span>{content}</span>
<br/>
<span>{likes}</span>
<br/>
<span>{rate()}</span>
<br/>
<span>{clickCount}</span>
</div>
</Profiler>
)
}
export default memo(CommentItem);
이미지처럼 내가 클릭하면 내가 클릭한 아이탬이 다시 그려진다.
이때 새로 그려지는 걸 방지 하기 위해 쓰는 것이 useMemo
이다.
import useMemo 받아 올 것!
const rate = useMemo(() =>{
console.log('rate');
return likes > 10 ? 'Good' : 'Bad'
},[likes]);
return (
<Profiler id="CommentItem" onRender={onRenderCallback}>
<div className='CommentItem' onClick={handleClick}>
<span>{title}</span>
<br/>
<span>{content}</span>
<br/>
<span>{likes}</span>
<br/>
<span>{rate}</span>
<br/>
<span>{clickCount}</span>
</div>
</Profiler>
)
여기서 item을 클릭하면 해당 item을 다시 그리지만 console.log(rate)가 찍히지 않게 된다.
useMemo는 어떤 것을 리턴하는 함수 리턴하는 값 반환된다. 계산을 통한 어떤 값을 메모제이션으 하는 것을 useMemo
useCallback은 특정한 함수를 메모제이션할 때는 useCallback을 사용
코드 궁금하시다면 github을 참고하세요!
위 내용들은 React 강의를 통한 내용을 정리한 것 입니다.