Help us understand the problem. What is going on with this article?

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

この記事は 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して何かするという処理を書くことは結構あると思うので,積極的に使用していきましょう

igrx
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした