6
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

intersection observerを使用したInfinite Scrollの実装

Posted at

この記事は CyberAgent 20 新卒 Advent Calendar 2019 の 22 日目です!
今回はタイトルにも書いてありますがintersection observerを使用してInfinite Scrollを実装したいと思います。

Intersection Observerとは

Intersection Observer API (交差監視 API) は、ターゲットとなる要素が、祖先要素もしくは文書の最上位のビューポートと交差する変更を非同期的に監視する方法を提供します。
https://developer.mozilla.org/ja/docs/Web/API/Intersection_Observer_API

useObserver

  • intersection observerのcustom hook
import React, { useEffect, useState, RefObject } from 'react';
function useObserver(
  ref: RefObject<HTMLDivElement>,
  ops?: IntersectionObserverInit
) {
  const [intersect, setIntersect] = useState<boolean>(false);
  useEffect(() => {
    function callback(entries: IntersectionObserverEntry[]) {
      entries.forEach(entry => {
        setIntersect(entry.isIntersecting);
      });
    }
    const observer = new IntersectionObserver(callback, ops);
    if (ref.current === null) {
      return;
    }
    observer.observe(ref.current);
    return () => {
      if (ref.current !== null) {
        observer.unobserve(ref.current);
      }
    };
  });
  return intersect;
}

使い方

export const Index: React.FC = () => {
  const target = React.useRef<HTMLElement>(null);
  const intersect = useObserver(target);

  return (
    <React.Fragment>
      <div style={{ position: 'fixed', top: 0, left: '50%' }}>
        {intersect ? 'visible' : 'not visible'}
      </div>
      <div style={{ height: 1000 }}></div>
      <div ref={target}></div>
    </React.Fragment>
  );
};
  • まず、ObserveしたいElementをuseObserverに渡すために、refを作成します。

    つぎに、ObserveしたいElementにpropsとしてrefを渡せば完了です。

    今回の例では、最初はrefを渡したDiv Elementが画面に表示されていないため、画面上部中央には、not visibleと表示されます。

    画面をスクロールして、refを渡したDiv Elementが画面に表示されると、visibleと表示されます。

infinite scroll

今回実装したもの

20191013001653.gif

ソースコード

import React, { useEffect, useState, RefObject } from 'react';
import axios from 'axios';
import './App.css';

interface Item {
  id: number;
  image: string;
}

const fetchItems = async (): Promise<Item[]> => {
  const { data } = await axios.get('http://localhost:3000/item');
  return data;
};

function useObserver(
  ref: RefObject<HTMLElement>,
  ops?: IntersectionObserverInit
) {
  const [intersect, setIntersect] = useState<boolean>(false);
  useEffect(() => {
    function callback(entries: IntersectionObserverEntry[]) {
      entries.forEach(entry => {
        setIntersect(entry.isIntersecting);
      });
    }
    const observer = new IntersectionObserver(callback, ops);
    if (ref.current === null) {
      return;
    }
    observer.observe(ref.current);
    return () => {
      if (ref.current !== null) {
        observer.unobserve(ref.current);
      }
    };
  });

  return intersect;
}

export const App: React.FC = () => {
  const [items, setItems] = useState<Item[]>([]);
  const target = React.useRef<HTMLDivElement>(null);
  const intersect = useObserver(target);

  useEffect(() => {
    async function fetch() {
      const data = await fetchItems();
      setItems(data);
    }
    fetch();
  }, []);

  useEffect(() => {
    async function fetch() {
      if (intersect) {
        const data = await fetchItems();
        setItems(prevItems => [...prevItems, ...data]);
      }
    }
    fetch();
  }, [intersect]);

  return (
    <React.Fragment>
      <div className="container">
        {items &&
          items.map((item) => {
            return (
              <React.Fragment key={item.id}>
                <div className="card">
                  <div className="card__item">
                    <img src={item.image}></img>
                  </div>
                </div>
              </React.Fragment>
            );
          })}
      </div>
      <div ref={target}>loading</div>
    </React.Fragment>
  );
};

export default App

おわりに

今回はintersection observerを使用してInfinite Scrollの実装を行いました.
intersection observerはその他にも,オフスクリーン時の画像の遅延読み込みやコンポーネントの遅延読み込みなどの使用法が考えられます.
要素をobserveして何かするという処理を書くことは結構あると思うので,積極的に使用していきましょう

6
2
0

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
6
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?