Flatlistの設定を最適化する
用語
-
VirtualizedList:
FlatList
の背後にあるコンポーネントです (React NativeによるVirtual List
の概念の実装)。 -
メモリ消費量 (Memory consumption): アプリのクラッシュにつながる可能性のある、リストに関する情報がどれだけメモリに格納されているかを示します。
-
応答性 (Responsiveness): アプリケーションがインタラクションに応答する能力。例えば、応答性が低いとは、コンポーネントをタッチしたときに、期待されるように即座に応答するのではなく、少し待ってから応答する状態を指します。
-
空白領域 (Blank areas):
VirtualizedList
がアイテムを十分に速くレンダリングできない場合、まだレンダリングされていないコンポーネントが空白として表示されることがあります。 -
ビューポート (Viewport): ピクセルにレンダリングされるコンテンツの可視領域です。
-
ウィンドウ (Window): アイテムがマウントされるべき領域で、通常はビューポートよりもはるかに大きいです。
Props
FlatList
のパフォーマンスを向上させるのに役立つプロパティのリストを以下に示します。
removeClippedSubviews
型 | デフォルト |
---|---|
Boolean | False |
true
の場合、ビューポート外のビューはネイティブのビュー階層から切り離されます。
メリット: これにより、ビューポート外のビューをネイティブのレンダリングおよび描画処理から除外することで、メインスレッドで費やされる時間を短縮し、フレーム落ちのリスクを低減します。
デメリット: この実装には、特に transform や絶対位置指定で複雑なことをしている場合に、コンテンツが欠落するなどのバグがある可能性があることに注意してください(主にiOSで観測されます)。また、ビューは解放されるのではなく、切り離されるだけなので、メモリを大幅に節約するわけではないことにも注意してください。
maxToRenderPerBatch
型 | デフォルト |
---|---|
Number | 10 |
これは VirtualizedList
のプロパティで、FlatList
を通して渡すことができます。スクロールごとにレンダリングされる次のアイテムのチャンクである、バッチごとにレンダリングされるアイテムの量を制御します。
メリット: より大きな数値を設定すると、スクロール時の視覚的な空白領域が少なくなることを意味します(フィルレートが向上します)。
デメリット: バッチあたりのアイテム数が多いと、JavaScriptの実行時間が長くなり、プレスなどの他のイベント処理をブロックする可能性があり、応答性が損なわれます。
updateCellsBatchingPeriod
型 | デフォルト |
---|---|
Number | 50 |
maxToRenderPerBatch
がバッチごとにレンダリングされるアイテムの量を指定するのに対し、updateCellsBatchingPeriod
はバッチレンダリング間の遅延をミリ秒単位で VirtualizedList
に伝えます(コンポーネントがウィンドウ内のアイテムをどれくらいの頻度でレンダリングするか)。
メリット: このプロパティと maxToRenderPerBatch
を組み合わせることで、例えば、より少ない頻度のバッチでより多くのアイテムをレンダリングしたり、より頻繁なバッチでより少ないアイテムをレンダリングしたりすることができます。
デメリット: バッチの頻度が低いと空白領域が発生する可能性があり、バッチの頻度が高いと応答性の問題が発生する可能性があります。
initialNumToRender
型 | デフォルト |
---|---|
Number | 10 |
最初にレンダリングするアイテムの数です。
メリット: すべてのデバイスで画面をカバーするアイテムの正確な数を定義します。これは初期レンダリングのパフォーマンスを大幅に向上させることができます。
デメリット: 低い initialNumToRender
を設定すると、特に初期レンダリング時にビューポートをカバーするには小さすぎる場合、空白領域が発生する可能性があります。
windowSize
型 | デフォルト |
---|---|
Number | 21 |
ここで渡される数値は測定単位で、1はビューポートの高さに相当します。デフォルト値は21です(ビューポートの上10、下10、そしてその間の1)。
メリット: windowSize
を大きくすると、スクロール中に空白スペースが表示される可能性が低くなります。一方、windowSize
を小さくすると、同時にマウントされるアイテムが少なくなり、メモリを節約できます。
デメリット: windowSize
を大きくすると、メモリ消費量が多くなります。windowSize
を小さくすると、空白領域が表示される可能性が高くなります。
リストアイテム
以下はリストアイテムコンポーネントに関するいくつかのヒントです。これらはリストの中核であり、高速である必要があります。
基本的なコンポーネントを使用する
コンポーネントが複雑であるほど、レンダリングが遅くなります。リストアイテムに多くのロジックやネストを避けるようにしてください。このリストアイテムコンポーネントをアプリで頻繁に再利用する場合は、大きなリスト専用のコンポーネントを作成し、できるだけ少ないロジックとネストで構成してください。
軽量なコンポーネントを使用する
コンポーネントが重いほど、レンダリングが遅くなります。重い画像を避けてください(リストアイテムには、できるだけ小さい切り抜きバージョンやサムネイルを使用してください)。デザインチームと相談し、リストにはできるだけ少ないエフェクト、インタラクション、情報を使用してください。それらはアイテムの詳細画面で表示するようにしましょう。
memo()
を使用する
React.memo()
は、コンポーネントに渡されるpropsが変更された場合にのみ再レンダリングされるメモ化されたコンポーネントを作成します。この関数を使用して、FlatList内のコンポーネントを最適化できます。
import React, {memo} from 'react';
import {View, Text} from 'react-native';
const MyListItem = memo(
({title}: {title: string}) => (
<View>
<Text>{title}</Text>
</View>
),
(prevProps, nextProps) => {
return prevProps.title === nextProps.title;
},
);
export default MyListItem;
この例では、`MyListItem` は `title` が変更された場合にのみ再レンダリングされるべきだと判断しました。比較関数を `React.memo()` の第二引数として渡すことで、指定されたpropが変更された場合にのみコンポーネントが再レンダリングされるようにします。比較関数がtrueを返した場合、コンポーネントは再レンダリングされません。
キャッシュされ最適化された画像を使用する
コミュニティパッケージ(@DylanVann氏によるreact-native-fast-imageなど)を使用して、よりパフォーマンスの高い画像を利用できます。リスト内の各画像は new Image()
インスタンスです。それが loaded
フックに速く到達するほど、JavaScriptスレッドが再び解放されるのが速くなります。
getItemLayout を使用する
すべてのリストアイテムコンポーネントが同じ高さ(または水平リストの場合は幅)を持つ場合、getItemLayoutプロパティを提供すると、FlatList
が非同期のレイアウト計算を管理する必要がなくなります。これは非常に望ましい最適化手法です。
コンポーネントのサイズが動的で、本当にパフォーマンスが必要な場合は、デザインチームにパフォーマンスを向上させるための再設計を検討してもらえないか相談することを検討してください。
keyExtractor または key を使用する
FlatList
コンポーネントにkeyExtractor
を設定できます。このプロパティは、キャッシュや、アイテムの並べ替えを追跡するためのReactのkey
として使用されます。
アイテムコンポーネントでkey
プロパティを使用することもできます。
renderItem で匿名関数を避ける
関数コンポーネントの場合、renderItem
関数を返されるJSXの外に移動させます。また、レンダリングごとに再作成されるのを防ぐために、useCallback
フックでラップするようにしてください。
クラスコンポーネントの場合、renderItem
関数をrender関数の外に移動させます。これにより、render関数が呼び出されるたびに再作成されることがなくなります。
const renderItem = useCallback(({item}) => (
<View key={item.key}>
<Text>{item.title}</Text>
</View>
), []);
return (
// ...
<FlatList data={items} renderItem={renderItem} />;
// ...
);