Closed7
[Scrap] React で無限スクロールする方法を調べる
スクロール可能にする
そもそも、HTML/CSS で一定領域をスクロール可能にするどうやるのってところから分かってなかった。
領域(height
, width
) を指定して、overflow: 'auto'
の指定でスクロール可能になるっぽい。
const InfiniteScrollPage = () => {
return (
<Box bg={'blue.500'} py={32}>
<Center>
<Box bg={'white'} w={80} overflow={'auto'} h={'300px'}>
{indexes.map(i => (
<Center key={i}>
<Text>index of {i}</Text>
</Center>
))}
</Box>
</Center>
</Box>
)
}
無限スクロールのパッケージどんなのがあるか
自前で実装するのは考えてないので、パッケージを使いたい。ググったり npm 検索したりして調べる。
react-infinite-scroll-component
結構使われてるっぽい。記事もたくさん出てくる。
react-infinite-scroller
結構使われてるっぽいし、記事もたくさん出てくるけど、最終アプデが三年前なのでメンテされてないのでは。使わないことにする。
react-virtualized
ピンポイントで無限スクロールが作れるというよりかは、表作ったり要素が多い画面をパフォーマンスよく実装するツールキット、っぽい印象。全然触ってないのでなんとも言えないけど、若干難しそうな気がする。
react-infinite-scroll-component を試す。
Next.js でも問題なく動く。
import React, { useState } from 'react'
import InfiniteScroll from 'react-infinite-scroll-component'
import { Box, Heading } from '@chakra-ui/react'
const ISC = () => {
const [items, updateItems] = useState(Array.from({ length: 40 }))
const fetchMoreData = () => {
setTimeout(() => {
updateItems(items.concat(Array.from({ length: 40 })))
}, 1500)
}
return (
<Box>
<InfiniteScroll
dataLength={items.length}
next={fetchMoreData}
hasMore={true}
loader={<Heading>Loading...</Heading>}
pullDownToRefresh={true}
pullDownToRefreshContent={<Heading>Pulling</Heading>}
refreshFunction={() => {
setTimeout(() => {
updateItems(Array.from({ length: 40 }))
}, 1500)
}}
>
{items.map((i, index) => (
<Box key={index}>div - #{index}</Box>
))}
</InfiniteScroll>
</Box>
)
}
export default ISC
ただ、画面の高さが長く、初期化時のアイテムが全部見えている状態になっていると、次のアイテムを呼び出す next
が走らないっぽい。
一瞬問題かもと思ったけど、総アイテム数が少ない & next
を呼び出す閾値を超えてるがことでnext
が無限に呼ばれ続ける、的な挙動よりはるかに扱いやすいし、実用的に思う。
react-virtualized
試そうと思ったが、行ごとに高さを計算しないといけないっぽい。ちゃんとやらないとうっかりミスしそう。オフの時間に触りたくない感がある。
素晴らしいブログ見つけた。
このブログ読む限り仮想 Window 使って無限スクロールやること自体、沼な感じがした。
実装コスト高い & バグりやすそう & 設計変更の追従面倒そう、ってだから必然性ない限り避けたい。
react-virtuoso
react-virtuoso
というのがあるみたいなので、試してみる。
- チャットとか上向きに伸びていく List にも対応しているよ、って書いてていい感じ。
-
increaseViewportBy
ってプロパティで viewport の仮想的な長さ調整できるのいい感じ
しかし、今のところ描画されないので、明日以降に持ち越し。
import { Heading } from '@chakra-ui/react'
import { Virtuoso } from 'react-virtuoso'
const items: number[] = [0, 1, 2, 3, 4, 5, 6, 8, 9, 10]
const VirtuosoList = () => {
return (
<Virtuoso<number>
style={{
background: 'blue',
}}
height={600}
width={800}
data={items}
totalCount={items.length}
itemContent={index => (
<Heading bg="yellow" color="black" key={index}>
Index of {index}
</Heading>
)}
/>
)
}
export default VirtuosoList
凡ミスだった。修正して問題なく動いたのでヨシ。
react-virtuoso
良さそう。
ただ、こういう最適化するの最後の最後だな、とも思った。
import { Center, Box, Heading } from '@chakra-ui/react'
import { Virtuoso } from 'react-virtuoso'
import * as React from 'react'
import { useState } from 'react'
function* generate(min: number, max: number) {
let currentValue = min
while (currentValue < max) yield currentValue++
}
const indexes = Array.from(generate(0, 10000))
const VirtuosoList = () => {
const [totalCount, updateTotalCount] = useState(100)
return (
<Box bg={'blue'} h={'100vh'} w={'100vw'}>
<Center h={'100%'} w={'100%'}>
<Box h={'50vh'} w={'50vw'} overflow={'auto'} bg={'white'}>
<Heading>Total count is {totalCount}</Heading>
<Virtuoso
totalCount={totalCount}
endReached={_ => updateTotalCount(totalCount + 100)}
increaseViewportBy={200}
itemContent={index => <div>Item {index}</div>}
/>
</Box>
</Center>
</Box>
)
}
export default VirtuosoList
結論
実際の Web サイトで実装したら色々変わりそうだけど、以下のが結論。
- 最初は
react-infinite-scroll-component
サクッと作る。 - パフォーマンスに問題ありそうだなってなったら、react-virtuoso を使う。
このスクラップは2021/09/23にクローズされました