본 내용은 아래 강의에 대한 복습내용입니다.
https://www.inflearn.com/course/%ED%95%9C%EC%9E%85-%EB%A6%AC%EC%95%A1%ED%8A%B8
한 입 크기로 잘라 먹는 리액트(React.js) : 기초부터 실전까지| 이정환 Winterlood - 인프런 강의
현재 평점 4.9점 수강생 13,117명인 강의를 만나보세요. 개념부터 독특한 프로젝트까지 함께 다뤄보며 자바스크립트와 리액트를 이 강의로 한 번에 끝내요. 학습은 짧게, 응용은 길게 17시간 분량
www.inflearn.com
useMemo는 리액트에서 성능 최적화(렌더링 최소화)를 위해 쓰이는 훅으로 값(value)을 메모이제이션(Memoization)한다,
연산 값을 저장해두었다가 의존값(dependency)이 바뀔 때만 다시 계산하는 기능이다.
예를 들어, 아래 코드에서 getAnalyzedDate() 함수를 먼저 봐보자.
이 함수는 전체적인 List 컴포넌트 내부에 위치해 있기 때문에,
이 컴포넌트가 리렌더링 될 때마다 새롭게 계산하게 된다.
단순히 검색어를 입력하여 리스트 컴포넌트가 리렌더링 될 때에도, todos배열의 길이가 변화하는 게 아니라 값이 바뀔리가 없는데고 리스트 컴포넌트가 새롭게 렌더링되기 때문에 getAnalyzedDate 내부에 있는 연산이 불필요하게 반복된다.
import { useState } from "react";
import "./List.css";
import TodoItem from "./TodoItem";
const List = ({todos, onUpdate, onDelete}) =>{
const [search,setSerch] = useState("");
const onChangeSearch = (e) =>{
setSerch(e.target.value);
}
const getFilterdData = () =>{
if(search===""){
return todos;
}
return todos.filter((todo)=>
todo.content.toLowerCase().includes(search.toLowerCase())
);
}
const filteredTodos =getFilterdData();
const getAnalyzedData = () =>{
const totalCount = todos.length;
const doneCount = todos.filter((todo)=>todo.isDone).length
const notDoneCount = totalCount - doneCount
return{
totalCount,
doneCount,
notDoneCount
}
}
const{totalCount, doneCount, notDoneCount} = getAnalyzedData();
return (
<div className="List">
<h4>Todo List ❤️</h4>
<div>total :{totalCount}</div>
<div>done : {doneCount}</div>
<div>notDone : {notDoneCount}</div>
<input
value={search}
onChange={onChangeSearch}
placeholder="검색어를 입력하세요" />
<div className="todos_wrapper">
{filteredTodos.map((todo)=>{
return <TodoItem
key={todo.id}
{...todo}
onUpdate={onUpdate}
onDelete ={onDelete}
/>
})}
</div>
</div>
);
};
export default List;
이때, 필요한 것이 useMemo다.
useMemo의 결과값은 첫번째 인자값인 콜백함수의 리턴값을 그대로 반환하며, 두번째 인자값인 의존성 배열(dependency array)에 있는 값이 바뀔 때만 계산을 하는 것이다.
const memoizedValue = useMemo(() => {
// 연산 로직
return 결과값;
}, [의존성배열]);
그래서 다시 위의 코드를 고쳐보면
기존의 getAnalyzedDate()의 로직을 useMemo의 콜백함수에 넣고, 이 연산은 todos라는 state값의 변화에만 영향을 받으므로 todos를 의존성배열에 넣어준 것이다.
그러면 검색어를 입력하여 리스트 컴포넌트가 계속해서 리렌더링 되더라도 불필요하게 count값들을 연산하는 과정이 일어나지 않게 된다.
import { useState, useMemo } from "react";
import "./List.css";
import TodoItem from "./TodoItem";
const List = ({todos, onUpdate, onDelete}) =>{
const [search,setSerch] = useState("");
const onChangeSearch = (e) =>{
setSerch(e.target.value);
}
const getFilterdData = () =>{
if(search===""){
return todos;
}
return todos.filter((todo)=>
todo.content.toLowerCase().includes(search.toLowerCase())
);
}
const filteredTodos =getFilterdData();
//첫번째 콜백함수의 결과값을 그대로 반환하고, []의존성배열에 담긴 값이 바뀔 때만 리렌더링
const{totalCount, doneCount, notDoneCount} = useMemo(()=>{
const totalCount = todos.length;
const doneCount = todos.filter((todo)=>todo.isDone).length
const notDoneCount = totalCount - doneCount
return{
totalCount,
doneCount,
notDoneCount
}
},[todos]) //
return (
<div className="List">
<h4>Todo List ❤️</h4>
<div>total :{totalCount}</div>
<div>done : {doneCount}</div>
<div>notDone : {notDoneCount}</div>
<input
value={search}
onChange={onChangeSearch}
placeholder="검색어를 입력하세요" />
<div className="todos_wrapper">
{filteredTodos.map((todo)=>{
return <TodoItem
key={todo.id}
{...todo}
onUpdate={onUpdate}
onDelete ={onDelete}
/>
})}
</div>
</div>
);
};
export default List;