841-biborokuWebフロントエンドの備忘録

仮想スクロールライブラリを使って記事一覧から特定の単語を含むタイトル記事を絞り込み表示する

React-Virtuoso、React-Window、React-Virtualizedの3つを同じ記事データを使って実装比べをしてみました。

3つとも複雑さはなく簡単に実装することができました。個人的には、React-Virtuosoがシンプルかつ要素の高さを自動で可変対応してくれる機能が備わっていて使いやすかったです。

仮想スクロールを実装

大量のダミーデータを使ってもよかったのですが、1200件記事(手持ちの記事30件×40)で実装しました。

↓スクロールするとDOMが更新されていることが分かります。スルスル・サクサク動きます。

React-Virtuoso

https://virtuoso.dev/

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;

React-Window

有名な仮想リストライブラリです。

https://react-window.vercel.app/#/examples/list/fixed-size

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年)
※()…最初のバージョン