LoginSignup
7
5

More than 1 year has passed since last update.

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

Last updated at Posted at 2021-11-09

Intersection ObserverAPIを使用して、無限スクロールを実装します。
取得するAPIはTMDBというものを使用します。

Intersection ObserverAPIとは

簡単に言うと、ユーザーが画面上をスクロールなどした時、特定の要素が、ブラウザ上の表示されている領域に対してどの位置にあるかを監視して教えてくれる機能を持っている交差監視APIです。

下記が詳しいです
https://ics.media/entry/190902/

API取得のロジック

import { useEffect, useState } from 'react'
import axios from 'axios'

export default function SearchMovies(query: any, pageNumber: any) {
  const [loading, setLoading] = useState<any>(true) // ローディングの判定
  const [error, setError] = useState<any>(false) // エラーを検知したらセットする
  const [movies, setMovies] = useState<any>([]) // 検索結果をセットする
  const [hasMore, setHasMore] = useState<any>(false) // 検索結果が残っているか判定する

  useEffect(() => {
    setMovies([])
  }, [query])

  // api呼び出し
  useEffect(() => {
    setLoading(true)
    setError(false)
      let cancel: any
      const apikey = 'hogehoge'
    axios({
      method: 'GET',
      url: 'https://api.themoviedb.org/3/search/movie',
      params: { api_key: apikey, query: query, page: pageNumber },
      cancelToken: new axios.CancelToken(c => cancel = c)
    }).then(res => {
      setMovies((prevMovies: any) => {
          return [...new Set([...prevMovies, ...res.data.results.map((b: any) => b.title)])]
      })
        setHasMore(res.data.results.length > 0)
        setLoading(false)
        console.log('then 成功です')
    }).catch(e => {
      if (axios.isCancel(e)) return
        setError(true)
        console.log('catch errorです')
    })
    return () => cancel()
  }, [query, pageNumber])
  return { loading, error, movies, hasMore }
}

画面描画

import React, { useState, useRef, useCallback } from 'react'
import SearchMovies from "./components/SeachMovies"


export default function App() {
  const [query, setQuery] = useState('') // 検索ワードをセットする
  const [pageNumber, setPageNumber] = useState(1) // ページ番号をセットする

  const {
    movies,
    hasMore,
    loading,
    error
  } = SearchMovies(query, pageNumber)

    // ref対象を監視して表示終わったら、ページ番号を増やす
  const observer: any = useRef()
    const lastMovieElementRef = useCallback(node => {
    if (loading) return
    if (observer.current) observer.current.disconnect()
        observer.current = new IntersectionObserver(entries => {
        if (entries[0].isIntersecting && hasMore) {
        setPageNumber(prevPageNumber => prevPageNumber + 1)
        }
    })
    if (node) observer.current.observe(node)
  }, [loading, hasMore])

    // 入力値をセットする
  function handleSearch(e: any) {
    setQuery(e.target.value)
    setPageNumber(1)
  }

  return (
    <>
        <input type="text" value={query} onChange={handleSearch}></input>
        {movies.map((movie: any, index: any) => {
          if (movies.length === index + 1) {
            return <div ref={lastMovieElementRef} key={movie}>{movie}</div>
          } else {
            return <div key={movie}>{movie}</div>
        }
      })}
      <div>{loading && 'Loading...'}</div>
      <div>{error && 'Error'}</div>
    </>
  )
}

上記を実装すると、入力値をqueryとして受け取り、検索結果を返してくれます。
スクロールすると、1ページ分ごとにAPIの結果が読み込まれて画面に描画されると思います。

7
5
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
7
5