Tanstack Virtualアプリケーター

🌐

この記事は DeepL によって翻訳されました。誤訳があれば教えてください!

Tanstack Virtualとは?

Tanstack Virtualは、大きなスクロール要素を効率的にレンダリングするための仮想化ライブラリです。

AnimatedImage.gif

どんなに多くの要素があっても、実際のDOMでは一定の数だけレンダリングする技術です。DOMにレンダリングされる要素が増えれば増えるほど、ある瞬間から性能が急激に低下するので、見せなければならない要素が多い場合、このような仮想化技法を適用することができます。

VirtualizeライブラリはTanstack以外にもreact-virtualized、react-windowなどがあります。

問題点

Image.png

従来は上のようにgridで構成されていて、viewportによって表示されるカード数が変わるようにしました。

Tanstack Virtualの例を見ると分かるように、Tanstack Virtualを使うとスクロールdivの中にアイテムをabsoluteで適用して、transformを使って位置を調整するようになっています。

つまり、従来の方法では親にVirtualizerをかぶせるだけでいいという問題ではありませんでした。Tanstack Virtualを使うと一つのrowやcolumnに一つの要素だけ入るようにする必要があります。

解決方法

既存のリストアイテムは1次元配列になっていて、これをmapを回してレンダリングしています。

const itemList = [item1, item2, item3, item4, item5, item6, item7];

return <div className='grid grid-cols-1 tablet:grid-cols-3 desktop: grid-cols-5'> >.
  itemList.map(item => <card item={item} />)
</div> </div

上で説明したように一つのrowに一つの要素だけ入れる必要があるので、下記のようにコードを変更しました。

let columnSize = 3; // 1, 3, 5

const chunkedItemList = chunkArray(itemList, columnSize);
// [[item1, item2, item3], [item4, item5, item6, item7]], [item7]]].

return <div> を返します。
  chunkedItemList.map(list => <div className='grid grid-cols-1 tablet:grid-cols-3 desktop: grid-cols-5'> > chunkedItemList.map(list => <div className='grid grid-cols-1 tablet:grid-cols-3 desktop: grid-cols-5'>)
    {list.map(item => <card item={item} />)} }
    </div>)
</div> </div> </div

既存のアイテムリストを1次元配列からcolumnSizeだけ小さい2次元配列にしました。

図で見てみるとこんな感じです。

Image.png

注意しなければならないのは一行に一つの要素だけ入れること、そしてその一つの要素の中に何個のアイテムを入れるかです!

実際のコードは下記のようになります。

const [columnSize, setColumnSize] = useState(COLUMN_SIZE.desktop);

const list = chunkArray(
  flatPages(data) ?? []、
  columnSize
)です;

const rowVirtualizer = useVirtualizer({ {
  count: list?.lengthgetScrollElement: () => document.getElementById("main-section")、
  estimateSize: () => 390overscan: 1、
}) を使用しています;

useEffect(() => { {
  if (isLabtop) setColumnSize(COLUMN_SIZE.labtop);
  else if (isMobile) setColumnSize(COLUMN_SIZE.mobile);
  else setColumnSize(COLUMN_SIZE.desktop);
}, [isLabtop, isMobile]);

(isLabtop、isMobile); }
  rowVirtualizer.getVirtualItems()?.map((virtualRow) => { {
    const row = list[virtualRow.index];
    if (!row) return nullreturn (
      <div
        key={virtualRow.key}。
        className={`absolute top-0 left-0 w-full`}
        style={{
          変換します:`translateY(${virtualRow.start}px)`、
        }}
      <div className="grid grid
        <div className="grid grid-cols-1 laptop:grid-cols-3 desktop:grid-cols-5 w-full gap-6 place-items-center items-stretch"> </div
          {row.map((item) => (
            <カード
              key={item.designId}
              item={item}
              isVault={isVault}
            </i> </i><i>。
          ))}
        </div> </div> </div
      </div> </div> </div
    )); </div> </div> </div> </div> </div> </div> </div
  });
}

結果、下記のように特定の数だけ実際のDOMに描画されるように仮想化することができました!

AnimatedImage.gif