Reactの仮想リストライブラリを調べたときのメモ
まとめ
- Slackのような仮想リストを実現するのは難しい
- いずれのライブラリも上方向へのスクロールは難しい
- 普通に使う分には、react-virtuosoはいいぞ
環境
React
Slackライクなメッセージアプリを作りたい
- 下方向に新しいメッセージが表示される
- 新規メッセージは下に追加される
- スレッドが存在している場合、スレッド元にジャンプできる
- 基本的にユーザは上方向へのスクロールを行う
- メッセージには画像や動画も添付でき、高さは不明
react-window
- doc: react-window
- npm: react-window - npm
- GitHub: GitHub - bvaughn/react-window: React components for efficiently rendering large lists and tabular data
- bundlephobia: react-window | BundlePhobia
特徴
- Reactのドキュメントに載っている
パフォーマンス最適化 – React - Rocket.Chatで使われている GitHub - RocketChat/Rocket.Chat: The ultimate Free Open Source Solution for team communications.
Pros
Cons
- 高さが不明なリストには対応していない :cry:
Support just-in-time measured content · Issue #6 · bvaughn/react-window · GitHub- 各要素の高さがわかっている場合はOK
react-virtualized
- doc: react-virtualized
- npm: react-virtualized - npm
- GitHub: GitHub - bvaughn/react-virtualized: React components for efficiently rendering large lists and tabular data
- bundlephobia: react-virtualized | BundlePhobia
特徴
- Reactのドキュメントに載っている
- Slackで使われている About This Version | Slack
Pros
- CellMeasurerを使うことで、要素の高さが不明でも仮想リストを扱える
react-virtualized/CellMeasurer.md at master · bvaughn/react-virtualized · GitHub- CellMeasurerCacheで高さのキャッシュを行う
- 画像などの読み込みで高さが変わる場合、
measure
を呼び出すことで再計算が行われる
- 一度高さの計算が行われると高さがキャッシュされるため、リストを高速にスクロールしても動作する
- 下方向へのスクロールはとてもスムーズ
Cons
- 上方向のスクロールがうまく行かない場合がある
- issueが存在するが、抜本的な解決には至っていない
Dynamic height + scrolling up (when the upper cells aren't loaded yet) · Issue #610 · bvaughn/react-virtualized · GitHub
まだ表示されていない要素の高さはdefaultHeight
から導出されるため、実際の計算結果と高さが異なるとスクロールがスムーズに行われなくなるscaleY(-1)
して逆向きに表示する方法はなるほどと思った
ただし、マウスによるスクロールも逆向きになる
- issueが存在するが、抜本的な解決には至っていない
measure
を呼び出すのが大変(な場合がある)- リストのComponentが複雑な場合、
measure
をバケツリレーすることになる
- リストのComponentが複雑な場合、
measure
の呼び出しに関わらず高さがキャッシュされるため、キャッシュが持っている高さが正しいとは限らない
dynamic-virtualized-list
- doc: READMEと実際の実装を確認してね
- npm: ありません
- GitHub: GitHub - mattermost/dynamic-virtualized-list: Dynamic virtualized list
- bundlephobia: ありません
特徴
Mattermostで使われている
react-windowを動的リストに対応させたもの
Pros
- 上方向のスクロールでも動く
Cons
- 初期読み込みに少し時間がかかる
- 高さの計算をするため、表示されていない領域の画像なども先読みしている
- 使い方に少しクセがある
- READMEに書かれている以外のCSSの設定が必要だった
- リストが画面上に複数あった場合、動作が不安定になるかも?
- keyやindexを正しく指定すれば問題ないかもしれない
- 表示とは逆向きにリストを渡す必要がある
- Mattermost向けのライブラリなので、今後APIが変わる恐れがある
- 型がない…
react-virtuoso
- doc: Getting Started with React Virtuoso | React Virtuoso
- npm: react-virtuoso - npm
- GitHub: GitHub - petyosi/react-virtuoso: The most powerful virtual list component for React
- bundlephobia: react-virtuoso | BundlePhobia
特徴
- 最近開発が活発
- メッセージアプリを作るときに便利なプロパティがある(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