TodoList.js
import React, { useCallback } from 'react';
import { List } from 'react-virtualized';
import './TodoList.scss';
import TodoListItem from './TodoListItem';
function TodoList({todos, onRemove, onToggle}) {
const rowRenderer = useCallback(({index, key, style}) => {
const todo = todos[index];
return (
<TodoListItem
todo={todo}
key={key}
onRemove={onRemove}
onToggle={onToggle}
style={style}
/>
);
}, [onRemove, todos, onToggle])
return (
<List
className='TodoList'
width={512}
height={513}
rowCount={todos.length}
rowHeight={57}
rowRenderer={rowRenderer}
list={todos}
style={{outline: 'none'}}
/>
)
}
export default React.memo(TodoList);
TodoListItem.js
import React from 'react';
import {
MdCheckBoxOutlineBlank,
MdRemoveCircleOutline,
MdCheckBox
} from 'react-icons/md';
import cn from 'classnames';
import './TodoListItem.scss';
function TodoListItem({todo, onRemove, onToggle, style}) {
const {id, text, checked} = todo;
return (
<div className="TodoListItem-virtualized" style={style}>
<div className='TodoListItem'>
<div className={cn('checkbox', {checked})} onClick={() => onToggle(id)}>
{checked ? <MdCheckBox /> : <MdCheckBoxOutlineBlank />}
<div className='text'>{text}</div>
</div>
<div className="remove" onClick={() => onRemove(id)}>
<MdRemoveCircleOutline />
</div>
</div>
</div>
)
}
export default React.memo(TodoListItem);
TodoListItem.scss
.TodoListItem-virtualized {
& + & {
border-top: 1px solid #dee2e6;
}
&:nth-child(even) {
background: #f8f9fa;
}
}
.TodoListItem {
padding: 1rem;
display: flex;
align-items: center;
.checkbox {
cursor: pointer;
flex: 1;
display: flex;
align-items: center;
svg {
font-size: 1.5rem;
}
.text {
margin-left: 0.5rem;
flex: 1;
}
&.checked {
svg {
color: #22b7cf;
}
.text {
color: #adb5bd;
text-decoration: line-through;
}
}
}
.remove {
display: flex;
align-items: center;
font-size: 1.5rem;
color: #ff6b6b;
cursor: pointer;
&:hover {
color: #ff8787;
}
}
}
react-virtualized 사용 이유
- 현재 컴포넌트에서 첫 렌덩링 때 2500개 컴포넌트 중 대부분은 스크롤 하기 전에 보이지 않는데 렌더링 일어남(비효율)
- todos에 변동이 있을 때도 다 TodoList 컴포넌트 내부의 map함수에서 배열의 처음부터 끝까지 컴포넌트로 반환(비효율)
- react-virtualized 사용하면 리스트 컴포넌트에서 스크롤되기 전에 보이지 않는 컴포넌트들은 렌더링 하지 않고 크기만 차지함
사용법
- npm i react-virtualized로 하면 오류가 나서 npm i --force로 해줘야함
- 개발자도구에서 최적화 원하는 컴포넌트 크기 측정
rowRenderer 함수와 List 컴포넌트
- List 컴포넌트에서 각 TodoItem을 렌더링 할 때 사용
- List 컴포넌트의 props로 설정해야함
- List 컴포넌트에는 해당 리스트의 전체 크기, 각 항목 높이, 각 항목을 렌더링 할 때 사용해야 하는 함수(rowRenderer), 배열을 props로 넣어줌(자동으로 최적화)
div.TodoListItem-virtualized로 감싸주기
- TodoListItem.js 파일에서 return을 위 className으로 감싸주기(짝수 리스트 색 변경 & 두 번째부터 윗 테투리 넣는 스타일 적용)
- TodoListItem.scss파일에서 .TodoListItem에 & + &와 &:nth-child(eve) 옮기기
결과
