본문 바로가기

FrontEnd

리액트 성능 최적화 : react-virtual

반응형

TLDR : 눈에 보이는 컴포넌트만 렌더링하자

데이터가 1억개라 생각하면, 이를 다 그리는 건 무의미하다.

사용자는 한번에 다 볼 수도 없고, 상호작용 할 수도 없다.

필요한 만큼만 그려서 최적화하자.

이것은 "virtualization" 혹은 "windowing"이라 하는 개념이며,경우에 따라 많은 데이터를 렌더링하는 컴포넌트의 성능을 개선할 수 있다.
 

React-virtual

https://tanstack.com/virtual/v3/docs/guide/00-introduction

1. 훅에 정보 전달하기

import { useVirtual } from "react-virtual";

const rowVirtualizer = useVirtual({
    size: items.length,
    parentRef: listRef,
    estimateSize: React.useCallback(() => 20, []),
    overscan: 10
});
.city-app ul li {
  height: 20px;
}
  • size : useVirtual에게 우리 리스트의 크기를 알려주고
  • parentRef : 목록의 스크롤 가능한 요소에 접근할 수 있도록 하며(스크롤 가능한 요소의 높이를 이용해 몇개를 보여줄지 계산)
  • estimateSize : 각 요소의 크기(px)를 알려줍니다.
  • overscan : 눈에 보이는 요소 전, 후로 갖고있을 요소 갯수를 의미합니다.

2. 리스트 컴포넌트에 정보 전달하기

리스트 역할을 하는 컴포넌트에 다음과 같은 정보를 전달합니다.

  • listRef : virtualizer에 전달된 리스트 컴포넌트의 레퍼런스 입니다.
  • virtualRows : 렌더링할 아이템입니다.
  • totalHeight : 아이템 갯수 * 픽셀 사이즈입니다.
    • 우리는 100개의 아이템을 각 20px로 그릴 예정이므로 2000px이 됩니다.
    <Menu
	  // ...
      listRef={listRef}
      virtualRows={rowVirtualizer.virtualItems}
      totalHeight={rowVirtualizer.totalSize}
    />

3.  스타일 적용하기

여기선 약간의 조작이 필요합니다.
리스트 역할을 하는 컴포넌트의 레이아웃에 position : relative를 적용합니다.
.city-app ul {
  height: 300px;
  width: 300px;
  overflow-y: scroll;
  background-color: #eee;
  padding: 0;
  list-style: none;
  position: relative;
}

그 다음, 리스트 역할을 하는 컴포넌트의 첫번째 자식 li 컴포넌트에 아래와 같이 totalHeight를 적용해줍니다.

이는 해당 컴포넌트가 다른 li 컴포넌트들의 공간을 미리 예약해두도록 합니다.

    <ul {...getMenuProps({ ref: listRef })}>
      <li style={{ height: totalHeight }} /> // here~~~~~~~~~~~~~~~~~
      {virtualRows.map(({ index, size, start }) => {
        const item = items[index];
        if (!item) return null;
        return (
          <ListItem
            key={item.id}
            getItemProps={getItemProps}
            item={item}
            index={index}
            isSelected={selectedItem?.id === item.id}
            isHighlighted={highlightedIndex === index}
            style={getVirtualRowStyles({ size, start })}
          >
            {item.name}
          </ListItem>
        );
      })}
    </ul>

또한, 해당 라이브러리는 absolute position을 사용하여, 시작점에서 밀어주는 방식을 사용하기 때문에 아래와 같이 스타일을 적용합니다.

const getVirtualRowStyles = ({ size, start }) => ({
  position: "absolute",
  top: 0,
  left: 0,
  width: "100%",
  height: size,
  transform: `translateY(${start}px)`
});
클릭한 컴포넌트로 스크롤하기
// 아래 함수를 사용합니다.
rowVirtualizer.scrollToIndex(highlightedIndex)

 

실행해보기 : 
https://react-performance.netlify.app/isolated/final/04.js

코드 보기 : 

https://github.com/kentcdodds/react-performance/blob/main/src/final/04.js

아래는 downshift와 react-virtual을 같이 사용한 예제인데,

클릭 시 filter가 기능하지 않고 있음. downshift쪽 문제인것 같음.

virtualization은 정상 동작.

참고

react-virtual

Introducing downshift 🏎 for React ⚛️ (kentcdodds.com)

Accessibility - Learn web development | MDN (mozilla.org)

 

반응형