🐕 react-virtualized
yarn add react-virtualized
전 yarn을 쓰고 있으니까 yarn add 명령어를 이용해 추가해주겠읍니다.
이 react-virtualized는 뭐하는 애냐면...
아직 보이지 않는 요소들은 스크롤 되기 전에
렌더링 하지 않고 크기만 차지하게 끔 만들어 줄 수 있다.
그리고 스크롤되면 자연스럽게 렌더링을 시킨다.
정말 좋네요... 이게 뭐 지연평간가 뭔가 하는 녀석이냐?(아닐수도)
👻최적화를 준비하자
얘를 제대로 써주려면 실제 요소들의 크기를 px단위로 알아내야한다고합니다.
개발자툴로 측정하면 뚝딱
어라...근데 선생님 선생님은 왜 480x24 에 패딩 16인데
전 463인거죠? 하하
아무튼 시키는대로 짜겠습니다.
🥶 TodoList.js 수정하기
import './TodoList.scss';
import { List } from 'react-virtualized';
import React, { useCallback } from 'react';
import TodoListItem from './TodoListItem';
const 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}
/>
);
});
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);
Link를 react-virtualized에서 불러오고
rowRenderer라는 함수를 만들어주는데 얘는
react-virtualized 의 List 컴포넌트에서 각 TodoItem을 렌더링할 때 사용한다.
그래서 이 함수를 List 컴포넌트의 props로 설정을 해줘야한다. 라고 하네요
그래서 아래 리턴문을 보면 rowRenderer 또한 props로 넘겨주는걸 볼 수 있군요
props로 넘겨줘야할게 꽤나 많은 느낌인데
아무튼 요 rowRenderer 함수엔 한개의 객체를 인자로 넣어주고
그 객체에는 index, key, style을 넣어준다 okay okay
List 컴포넌트에는 해당 리스트의 전체 크기와 각 항목의 높이
각 항목을 렌더링 할 때 사용해야 하는 함수 , 배열을 props로 넣어줘야한다.
그러면 알아서 props를 사용해서 List 컴포넌트가 최적화를 해준다. 아하
그럼 rowRenderer에는
List 컴포넌트의 렌더링 할 때 사용하는 함수 |
객체 타입으로 값을 넘겨주는데 |
index |
key |
style |
요렇게 들어가줘야 하고
rowRenderer는 html 요소를 반환해야한다.
왜냐면
List 컴포넌트에 들어가야 하는 props |
리스트의 전체 크기 |
각 항목의 높이 |
각 항목을 렌더링 할 때 사용해야 하는 함수(여기선 rowRenderer) |
배열(list) |
요렇게 넣어줘야된다는거군요
근데 rowRenderer에 들어가는 저 객체가 심히 이해가 안된다 그죠
rowRenderer 함수의 인자로 전달되는 객체는 React-virtualized에서 List 컴포넌트가 호출할 때 생성됩니다.
이 객체는 List 컴포넌트가 렌더링할 행(row)에 대한 정보를 포함하고 있습니다.
index: List가 렌더링할 행의 인덱스입니다.
key: React 컴포넌트가 렌더링할 때 각각의 엘리먼트에 대해 고유한 식별자를 지정하기 위한 속성입니다.
style: 해당 행을 렌더링하는 데 필요한 스타일을 나타냅니다.
따라서 rowRenderer 함수는 이 객체에서 index를 추출하여 todos 배열에서 해당하는 todo를 가져온 다음,
TodoListItem 컴포넌트에 전달합니다. key와 style은 TodoListItem 컴포넌트의 props로 사용됩니다.
이렇게 하면 List 컴포넌트가 효율적으로 가상화 리스트를 생성하면서, 필요한 행만 렌더링할 수 있게 됩니다.
흠... 챗지피티말로는 이렇다는데 잘 감이 안와서 대체 저게 무슨 문법인가 싶어
직접 고차함수와 콜백함수를 만들어서 저 문법과 유사하게 코드를 작성해봤습니다.
const hi = ({ index, key, style }) => {
console.log(index);
console.log(key);
console.log(style);
};
const highorder = (callback) => {
let obj = {
index: '안녕하세요 전 인덱스에요',
key: '안녕하세요 전 키에요',
style: '안녕하세요 전 스타일이에요',
};
callback(obj);
};
highorder(hi);
안녕하세요 전 인덱스에요
안녕하세요 전 키에요
안녕하세요 전 스타일이에요
너...왜 되냐???
뭐냐????
콜백 매개변수에 바로 디스트럭처링 할당을 때리는게 진짜 된다고???
하면서 챗지피티한테 물어보니까 된다네요...
대신 당연하게도 객체 디스트럭처링 할당이니 프로퍼티 키가 같은 경우만 받아와진다는 답변
그러니까 결국 List 컴포넌트는 인자로 객체를 받으니까
obj.index , obj.key , obj.style로도 접근해도 크게 상관은 없겠네요?
다만 가독성이 좀 떨어질뿐?
const rowRenderer = useCallback(({ index, key, style }) => {
const todo = todos[index];
console.log('전인덱스에요', index);
console.log('전키에요', key);
console.log('전스타일이에요', style);
return (
<TodoListItem
todo={todo}
key={key}
onRemove={onRemove}
onToggle={onToggle}
style={style}
/>
);
});
궁금해서 콘솔로그를 다 넣어봤는데
이런식으로 출력이 되네요
amazing...
🌞이제 TodoListItem을 수정해보자
TodoListItem.scss에 가서
.TodoListItem-virtualized {
& + & {
border-top: 1px solid #dee2e6;
}
&:nth-child(even) {
background: #f8f9fa;
}
}
최상단에 위 코드를 추가해줍니다.
&는 sass,scss 문법에서 사용되는 특별한 식별자입니다.
&는 부모(상위) 선택자를 참조할 때 사용하는데
&를 사용하면 상위 선택자와 현재 선택자를 결합할 수 있다는 특징이 있습니다.
현재 선택자의 인접한 형제 요소들 중에서
현재 선택자와 같은 요소들을 선택합니다.
따라서 & + &의 색을 red로 바꿔주면 이렇습니다.
&:nth-child(even) {
background: #f8f9fa;
}
요건 요 nth-child 부분의 백그라운드를 바꿔준 결과입니다.
짝수부분만 색이 변하는게 눈에 들어오죵
요 문법은 짝수인 부분만 선택하고자 할 때 사용한다네요
그럼 홀수만 보여주고 싶으면 어캄?
&:nth-child(odd) {
background: #f8f9fa;
}
odd로 바꿔주면 된다~
import {
MdCheckBoxOutlineBlank,
MdCheckBox,
MdRemoveCircleOutline,
} from 'react-icons/md';
import './TodoListItem.scss';
import cn from 'classnames';
import React from 'react';
const 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);
저 위에서 수정한 코드를 저장하고 나면
마치 순간이동을 하는거마냥 컴포넌트가 잔상을 보여주면서
절 능욕합니다.
당황하지 말고 하위 컴포넌트를 수정해주는 것으로 문제를 해결 할 수 있습니다.
<div className="TodoListItem-virtualized" style={style}>
style props를 추가로 받아와주고
최상위 html요소에 위 div를 추가해주면 됩니다.
🐶마치며
물론 쉽진 않았지만 고작 이정도 처리만으로도
엄청난 성능향상을 기대할 수 있다니 정말 라이브러리 최고...
'react' 카테고리의 다른 글
immer를 이용해 깊은 복사를 케이크처럼 간단히 먹자 (1) | 2023.04.08 |
---|---|
React Router를 이용해보자 근데 실습을 곁들인 (0) | 2023.04.07 |
리액트의 Context API를 알아보자.. (0) | 2023.04.04 |
useEffect를 async와 함께 사용할 때 유의할 점 (0) | 2023.03.31 |
리액트의 SPA (SIngle Page Application) (0) | 2023.03.28 |