仮想スクロールライブラリを使って記事一覧から特定の単語を含むタイトル記事を絞り込み表示する
React-Virtuoso、React-Window、React-Virtualizedの3つを同じ記事データを使って実装比べをしてみました。
3つとも複雑さはなく簡単に実装することができました。個人的には、React-Virtuosoがシンプルかつ要素の高さを自動で可変対応してくれる機能が備わっていて使いやすかったです。
仮想スクロールを実装
大量のダミーデータを使ってもよかったのですが、1200件記事(手持ちの記事30件×40)で実装しました。
↓スクロールするとDOMが更新されていることが分かります。スルスル・サクサク動きます。

React-Virtuoso
import debounce from "lodash/debounce"; // lodashのdebounce関数を使う
import { Virtuoso } from "react-virtuoso";
const Filters = ({ posts }: PostProps) => {
const [filter, setFilter] = useState("");
const [filteredPosts, setFilteredPosts] = useState(posts);
useEffect(() => {
const delayedFilterPosts = debounce(() => {
const filtered = posts.filter((post) =>
post.title.toLowerCase().includes(filter.toLowerCase())
);
setFilteredPosts(filtered);
}, 300);
delayedFilterPosts();
return () => {
delayedFilterPosts.cancel();
};
}, [filter, posts]);
const handleInputChange = (e: ChangeEvent<HTMLInputElement>) => {
setFilter(e.target.value);
};
return (
<>
<input
type="text"
placeholder="word..."
value={filter}
onChange={handleInputChange}
style={inputText}
/>
<Virtuoso
style={list}
data={filteredPosts}
totalCount={filteredPosts.length}
itemContent={(index, post) => (
<p key={index}>{post.title}</p>
)}
/>
</>
);
};
export default Filters;
import { FixedSizeList as List } from "react-window";
const Row = ({
index,
style,
}: {
index: number;
style: React.CSSProperties;
}) => <p style={style}>{filteredPosts[index].title}</p>;
return (
~
<List
height={500}
itemCount={filteredPosts.length}
itemSize={30}
width={"100%"}
>
{Row}
</List>
~
)
React-Virtualized
React-windowと同じ方が制作されていて、React-Windowとの違いも紹介されています。
https://bvaughn.github.io/react-virtualized/#/components/Masonry
import { List } from "react-virtualized";
const rowRenderer = ({
index,
key,
style,
}: {
index: number;
key: string;
style: React.CSSProperties;
}) => (
<div key={key} style={style}>
{filteredPosts[index].title}
</div>
);
return (
~
<List
width={600}
height={400}
rowCount={filteredPosts.length}
rowHeight={30}
rowRenderer={rowRenderer}
/>
~
)
<input>に入力したテキストで記事を絞り込む
onChangeイベントを発生させるので、過剰に発生させないようにするためlodashのdebounce関数を使用して制限しました。
// debounce関数で処理を間引く
const delayedFilterPosts = debounce(() => {
// フィルタリング処理
const filtered = posts.filter((post) =>
post.title.toLowerCase().includes(filter.toLowerCase())
);
setFilteredPosts(filtered);
}, 300); // 300msの遅延を設定
delayedFilterPosts(); // 初回レンダリング時に実行
debounce関数は、指定された関数を遅延させる関数を返します。この場合、300ミリ秒の遅延を設定しているので、delayedFilterPostsは300ミリ秒ごとに実行されます。これにより、filterPosts関数が短時間に連続して呼び出された場合に、最後の呼び出しのみが実際に実行され、過剰な処理を防ぎます。

ChatGPTに聞いてみた
機能 / ライブラリ | React-Virtualized | React-Window | React-Virtuoso |
---|---|---|---|
インストールの簡易さ | ⭐⭐⭐ | ⭐⭐⭐⭐ | ⭐⭐⭐⭐ |
コミュニティのサポート | ⭐⭐⭐ | ⭐⭐⭐ | ⭐⭐ |
性能 | ⭐⭐⭐⭐ | ⭐⭐⭐⭐ | ⭐⭐⭐⭐ |
メモリ使用量 | ⭐⭐⭐ | ⭐⭐⭐ | ⭐⭐⭐⭐ |
カスタマイズ可能性 | ⭐⭐⭐ | ⭐⭐⭐⭐ | ⭐⭐⭐ |
レンダリングの柔軟性 | ⭐⭐⭐ | ⭐⭐⭐⭐ | ⭐⭐⭐⭐ |
サポートされている | ⭐⭐⭐⭐ | ⭐⭐⭐ | ⭐⭐⭐⭐ |
ドキュメントの質 | ⭐⭐⭐⭐ | ⭐⭐⭐ | ⭐⭐⭐⭐ |
セルの可変高さサポート | ⭐⭐⭐ | ⭐⭐⭐ | ⭐⭐⭐⭐ |
ライブラリのサイズ | ⭐⭐⭐ | ⭐⭐⭐⭐ | ⭐⭐⭐⭐ |
ChatGPTに聞いたデメリット
React-Virtualized
- メモリ使用量がやや多い
- セルの再利用が不十分
React-Window
- セルの可変高さサポートがやや不十分
- ライブラリのサイズがやや大きい
React-Virtuoso
- コミュニティのサポートが限られている
他にもChatGPTに聞いてみた
📏メモリ使用量が小さい順
React-Window < React-Virtuoso < React-Virtualized
📚ライブラリのサイズが小さい順
React-Virtuoso < React-Window < React-Virtualized
🌎普及度(推測だそうです)
React-Window(2018年)> React-Virtualized(2015年)> React-Virtuoso(2020年)
※()…最初のバージョン