LoginSignup
1
0

More than 1 year has passed since last update.

preactで無限スクロール実装する

Last updated at Posted at 2022-03-24

概要

reactで無限スクロールするなら、ライブラリがたくさんある
https://github.com/danbovey/react-infinite-scroller

ただ、今回preactで開発していて、どうせならreactライブラリ使わずに簡単にできないかを考えた。

実装

下記の実装を作成。

import { h } from "preact";
import style from "./style.module.css";

/**
 *
 * @param loadMore scroll時に実施するmethod
 * @param children スクロール対象の要素
 * @param height スクロール範囲の高さ
 * @param margin 下から何pxの部分でロードをかけるかの値。default:20
 * @param hasMore 追加で読み込むかの判別flag。fefault:true
 * @param pageStart 追加ロードで読み込むpage。default:0
 * @returns
 */
const InfiniteScroll = ({
  loadMore,
  children,
  height,
  margin = 20,
  hasMore = true,
  pageStart = 0,
}: {
  loadMore: (page: number) => void;
  children: h.JSX.Element;
  height: number;
  margin?: number;
  hasMore?: boolean;
  pageStart?: number;
}) => {
  const onScrollHandler: h.JSX.UIEventHandler<HTMLDivElement> = (e) => {
    if (!hasMore) return;
    if (
      e.currentTarget.scrollHeight -
        e.currentTarget.offsetHeight -
        e.currentTarget.scrollTop <
      margin
    ) {
      loadMore(pageStart + 1);
    }
  };

  return (
    <div
      class={style["scroll-component"]}
      style={{ height: height }}
      onScroll={onScrollHandler}
    >
      {children}
    </div>
  );
};

export default InfiniteScroll;

cssは下記の通り。

.scroll-component {
  overflow-y: scroll;
}

loadMoreにはpageを受け取って追加読み込みする関数を渡す
childrenInfiniteScrollの子要素としてセットし、無限読み込みしたいlistなどを配置する
heightはスクロールする要素の高さ
marginは下から何pxの部分で追加読み込みするか決める値
hasMoreは追加読み込みするかどうかの条件
pageStartは読み込むページの初期値。追加読み込みするときは、pageStart+1のページを読み込む。

サンプル

下までスクロールされたら追加読み込みしてlistを増やすサンプルを示す。
サンプルコードなので、handlerやテストデータの作り方は雑です。

import h from "preact";
import { useState } from "preact/hooks";
import InfiniteScroll from "./infinite-scroll";

export const Sample = () => {
  const initTestData = [
    { key: "TestKey", value: "TestValue" },
    { key: "TestKey", value: "TestValue" },
    { key: "TestKey", value: "TestValue" },
    { key: "TestKey", value: "TestValue" },
    { key: "TestKey", value: "TestValue" },
    { key: "TestKey", value: "TestValue" },
    { key: "TestKey", value: "TestValue" },
    { key: "TestKey", value: "TestValue" },
    { key: "TestKey", value: "TestValue" },
  ];
  const [list, setList] =
    useState<{ key: string; value: string }[]>(initTestData);
  const [hasMore, setHasMore] = useState<boolean>(true);
  const [currentPage, setCurrentPage] = useState<number>(0);
  const loadMore = (page: number) => {
    const testData = initTestData.map((data) => {
      return { key: data.key + page, value: data.value + page };
    });
    setList([...list, ...testData]);
    setCurrentPage(page);
  };
  const reset = () => {
    setCurrentPage(0);
    setList(initTestData);
  };
  return (
    <div>
      <button onClick={reset}>リセット</button>
      <button
        onClick={() => {
          setHasMore(false);
        }}
      >
        読み込み停止
      </button>
      <InfiniteScroll
        loadMore={loadMore}
        hasMore={hasMore}
        margin={5}
        pageStart={currentPage}
        height={300}
      >
        <div style={{ border: "solid 1px" }}>
          <ul>
            {list.map((l) => (
              <li>
                {l.key}:{l.value}
              </li>
            ))}
          </ul>
        </div>
      </InfiniteScroll>
    </div>
  );
};


  • 最初は初期値だけlistに入っている
    スクリーンショット 2022-03-24 13.04.23.png

  • 下までスクロールすると追加でlistに追加されていく

スクリーンショット 2022-03-24 13.05.57.png

  • 読み込み停止(hasMore=false)すると下まで行っても追加読み込みしない

スクリーンショット 2022-03-24 13.06.16.png

まとめ

やってみたらすごい少ない行数で無限スクロールが実現できました。

また、ほぼreactと同じ使い方できるので、reactのライブラリを使わず自作でカスタムしたい人にも参考になるかもです。

ライブラリに頼りすぎるとコーディングスキル落ちそうだから簡単な実装程度なら自力で描いてかないとダメだね。

1
0
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0