概要
高さが動的な要素を含むFlatListで特定位置までのスクロールを実装する際に 、詰まった点が少しあったので備忘録を残しておきます。
ScrollToIndex ではなく ScrollToOffset を使用する
高さが静的な要素のみの場合は scrollToIndex を使用しても問題ないです。
要素の数が多く一画面に収まらない場合には getItemLayout を一緒に用いるのも忘れないようにしましょう。
上記を用いることで、数百個のアイテムを持つリストのパフォーマンスを大幅に向上させることができます。
getItemLayout={(data, index) => (
{length: ITEM_HEIGHT, offset: ITEM_HEIGHT * index, index}
)}
今回は高さが動的な要素を含むので、getItemLayoutでの特定の位置までの計算が上手くいかず、結果的に ScrollToIndex を使用するべきではないと判断しました。
その代替案として ScrollToOffsetを使用することにしました。
ScrollToOffsetは、特定位置までのオフセット(基準点からの位置)を指定するだけで、その位置までのスクロールを実装することができます。
実装例
import { useEffect, useRef } from 'react';
import { FlatList } from 'react-native';
const Smaple: React.FC = () => {
const ref = useRef<FlatList>(null);
useEffect(() => {
ref.current?.scrollToOffset({
offset:
// NOTE: offsetの高さ(内部で計算可能 ex index * LIST_HEIGHT)
animated: false,
});
});
return (
<FlatList
ref={ref}
data={data}
renderItem={({ item }) => item}
keyExtractor={(item): string => item}
/>
);
};
上から順にコード内容を解説していきます。
まず、useRef を用いることで FlatList にアクセスして ScrollToOffset 関数を使用できるようにしています。
次に関数の内容に関してです。
FlatListの型定義を見てみましょう。(他の関数も定義されているが長くなるので、 scrollToOffset の箇所のみ抜粋)
export class FlatList<ItemT = any> extends React.Component<FlatListProps<ItemT>> {
/**
* Scroll to a specific content pixel offset, like a normal `ScrollView`.
*/
scrollToOffset: (params: { animated?: boolean | null; offset: number }) => void;
上記の型定義からわかるように、ScrollToOffset は2つの引数を取ります。
offset は必ず指定する必要があります。
スクロールしたい箇所までのオフセットを指定します。(内部で計算可能 ex index * LIST_HEIGHT)
animated に関してはオプショナルで必ず指定する必要はないです。(デフォルトの設定は true)
useEffect を使用しているのは、関数の実行タイミングを調整するためです。
前提として useEffect に渡された関数はレイアウトと描画の後で遅延されたイベントとして実行されます。
今回は動的な高さを持つ要素を含むリストでのスクロールなので、レンダリング前に上記関数を実行すると、特定位置までのオフセットの値を正確に取得できなかったです。(オフセットの値を LIST_HEIGHT (固定値) * Index ( Redux から取得した値)で指定していたため。)
今回の実装例では、本関数を使用するファイル内で定義することを想定していますが、カスタムフックとして別ファイルに抜き出すことをおすすめします。
その際には引数としてref:RefObject<FlatList>
を受け取るようにすれば良いでしょう。
まとめ
今回はFlatListで特定位置までのスクロールに関してまとめました。
- 高さが静的な要素のみの場合は scrollToIndex を使用しても問題なし。
- 高さが動的な要素を含む場合は scrollToOffset を使用する。
場合によっては ScrollToOffset でも上手くいかない場合はあると思います。
その際には公式ドキュメントをしっかり読んで、それぞれの関数の役割を理解することから始めましょう。
参考
ReactNative 公式ドキュメント
https://reactnative.dev/docs/flatlist