tmegos blog

Web developer

Reactの仮想リストライブラリを調べたときのメモ

まとめ

  • Slackのような仮想リストを実現するのは難しい
  • いずれのライブラリも上方向へのスクロールは難しい
  • 普通に使う分には、react-virtuosoはいいぞ

環境

React
Slackライクなメッセージアプリを作りたい

  • 下方向に新しいメッセージが表示される
  • 新規メッセージは下に追加される
  • スレッドが存在している場合、スレッド元にジャンプできる
  • 基本的にユーザは上方向へのスクロールを行う
  • メッセージには画像や動画も添付でき、高さは不明

react-window

特徴

Pros

Cons

react-virtualized

特徴

Pros

  • CellMeasurerを使うことで、要素の高さが不明でも仮想リストを扱える
    react-virtualized/CellMeasurer.md at master · bvaughn/react-virtualized · GitHub
    • CellMeasurerCacheで高さのキャッシュを行う
    • 画像などの読み込みで高さが変わる場合、measureを呼び出すことで再計算が行われる
  • 一度高さの計算が行われると高さがキャッシュされるため、リストを高速にスクロールしても動作する
  • 下方向へのスクロールはとてもスムーズ

Cons

  • 上方向のスクロールがうまく行かない場合がある
  • measureを呼び出すのが大変(な場合がある)
    • リストのComponentが複雑な場合、measureをバケツリレーすることになる
  • measureの呼び出しに関わらず高さがキャッシュされるため、キャッシュが持っている高さが正しいとは限らない

dynamic-virtualized-list

特徴

  • Mattermostで使われている

  • react-windowを動的リストに対応させたもの

Pros

  • 上方向のスクロールでも動く

Cons

  • 初期読み込みに少し時間がかかる
    • 高さの計算をするため、表示されていない領域の画像なども先読みしている
  • 使い方に少しクセがある
    • READMEに書かれている以外のCSSの設定が必要だった
  • リストが画面上に複数あった場合、動作が不安定になるかも?
    • keyやindexを正しく指定すれば問題ないかもしれない
  • 表示とは逆向きにリストを渡す必要がある
  • Mattermost向けのライブラリなので、今後APIが変わる恐れがある
  • 型がない…

react-virtuoso

特徴

  • 最近開発が活発
  • メッセージアプリを作るときに便利なプロパティがある(Prosで後述)

Pros

  • メッセージアプリを作るときに便利なプロパティがある
    • initialTopMostItemIndex:初期表示の要素の指定
    • followOutput:最新の要素に追従してスクロールする
  • 最小限の記述で仮想リストが実現できる
  • 画像などの読み込みで表示中に高さが可変になっても、ライブラリ側で高さ変更を検出して反映してくれる
  • ライブラリがhooks + TypeScriptで書かれている :tada:

Cons

  • 高さがキャッシュされないので、スクロール中にスタックする場合がある
    • 画像を含む要素が画面外に出た場合、画像が消えて高さが再計算されてしまう
    • 画像がある(これをリストAとする) → 高さが増えてスクロールがずれる → ずれた結果リストAが画面外に追い出される → リストAから画像が消えて再計算される → 高さが減ってスクロールがずれる → ずれた結果リストAが画面内へ → はじめに戻る…
  • Safariでカクつく

調査していたときのメモ

  • 下方向へのスクロールはライブラリの力を借りて実現できるんだけどなー
  • Twitterって画像の高さを固定してるね
  • TweetDeck見てると上方向のスクロールしているときに急に最新に飛ぶことない?
  • Slackってスクロールがなめらかですごいなと思った
    • APIを見ると画像の高さが入っていたので、仮想リストを実現するためにはある程度高さのヒントが無いと難しいのかも
  • Slack、Twitterとかに慣れてると、スムーズに動くのが当たり前みたいになっているので、カクつくと「えええええ」ってなりそうと思った

試したバージョン

  • react-window: 1.8.6
  • react-virtualized: 9.22.3
  • dynamic-virtualized-list: master (8b239f6)
  • react-virtuoso: 1.3.1