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の結果が読み込まれて画面に描画されると思います。