【React Native】FlatList.getScrollableNode()徹底解説:エラーと解決策

2025-05-31

FlatList.getScrollableNode() とは?

FlatList.getScrollableNode()は、React NativeのFlatListコンポーネントが内部的に持っているスクロール可能なDOMノード(Webの場合)またはネイティブのスクロールビュー(モバイルアプリの場合)への参照を取得するためのメソッドです。

FlatListは、大量のデータを効率的に表示するために、内部的にScrollViewまたはVirtualizedList(さらにそのベースとなるScrollView)を使用しています。getScrollableNode()は、この内部のスクロール可能な要素に直接アクセスする必要がある場合に使用されます。

なぜ必要なのか?

通常、FlatListのスクロール操作は、scrollToIndexscrollToOffsetなどのFlatList自身が提供するメソッドを使うことで実現できます。しかし、以下のような特殊なケースで、getScrollableNode()が必要になることがあります。

  • デバッグや高度なカスタマイズ
    FlatListの内部動作を深く理解し、必要に応じてカスタマイズしたい場合に、その基盤となるスクロール要素にアクセスするために使用されることがあります。
  • 特定のネイティブ機能へのアクセス
    アニメーションライブラリなどで、スクロールビューのネイティブな要素に直接アクセスして、より複雑なアニメーションや操作を行いたい場合。
  • ネイティブのスクロールイベントリスナーの追加
    FlatListが提供していないような、より低レベルなスクロールイベントをリッスンしたい場合。

getScrollableNode()は、FlatListのインスタンスから呼び出します。通常、refを使ってFlatListのインスタンスにアクセスします。

import React, { useRef } from 'react';
import { FlatList, View, Text, Button } from 'react-native';

const data = Array.from({ length: 50 }, (_, i) => ({ id: String(i), title: `Item ${i}` }));

function MyFlatList() {
  const flatListRef = useRef(null);

  const handleGetScrollableNode = () => {
    if (flatListRef.current) {
      const scrollableNode = flatListRef.current.getScrollableNode();
      console.log('スクロール可能なノード:', scrollableNode);
      // ここで、取得したscrollableNodeに対して何らかの操作を行うことができます
      // たとえば、ネイティブのイベントリスナーを追加するなど
    }
  };

  return (
    <View style={{ flex: 1 }}>
      <Button title="Get Scrollable Node" onPress={handleGetScrollableNode} />
      <FlatList
        ref={flatListRef}
        data={data}
        renderItem={({ item }) => (
          <View style={{ padding: 20, borderBottomWidth: 1, borderBottomColor: '#ccc' }}>
            <Text>{item.title}</Text>
          </View>
        )}
        keyExtractor={(item) => item.id}
      />
    </View>
  );
}

export default MyFlatList;
  • 通常、UIの直接的な操作はReactの宣言的な方法で行うべきであり、このような低レベルなノードへの直接アクセスは、本当に必要な場合に限って検討すべきです。
  • 取得できるノードの型は、実行されているプラットフォーム(iOS/Android)やReact Nativeのバージョンによって異なる場合があります。
  • getScrollableNode()は、React Nativeの内部実装に依存する可能性があり、将来のバージョンで変更される可能性があります。できる限りFlatListが提供する公式API(scrollToIndexなど)を使用することが推奨されます。


FlatList.getScrollableNode() に関連するよくあるエラーとトラブルシューティング

Cannot read property 'getScrollableNode' of null または undefined

これは最も一般的なエラーで、FlatListref がまだ設定されていないか、FlatList コンポーネントがマウントされる前に getScrollableNode() を呼び出そうとした場合に発生します。

原因

  • FlatList が条件付きでレンダーされており、まだ画面に表示されていない場合。
  • useEffect のクリーンアップ関数内で getScrollableNode() を呼び出そうとした場合(既にコンポーネントがアンマウントされている可能性がある)。
  • ref がまだ null の状態(コンポーネントがまだレンダーされていない、またはアンマウントされた後など)。

トラブルシューティング

  • 遅延実行
    必要に応じて、setTimeout などで呼び出しを少し遅らせることを検討します。ただし、これは根本的な解決策ではなく、応急処置として使うべきです。

  • ライフサイクルイベントの考慮
    useEffect の中で getScrollableNode() を呼び出す場合は、コンポーネントがマウントされた後であることを保証するために、依存配列を空にして一度だけ実行されるようにするか、適切なタイミングで呼び出すように設計します。

  • ref.current の確認
    getScrollableNode() を呼び出す前に、必ず flatListRef.currentnullundefined でないことを確認してください。

    const flatListRef = useRef(null);
    
    const handleScroll = () => {
      if (flatListRef.current) { // ここでnullチェックを行う
        const scrollableNode = flatListRef.current.getScrollableNode();
        // ... 処理
      } else {
        console.warn("FlatList ref is not available yet.");
      }
    };
    

予期せぬ動作またはネイティブモジュールのエラー

getScrollableNode() が返すオブジェクトは、Web 環境(React Native for Web)ではDOMノード、iOS/Android ではネイティブのビューインスタンスへの参照です。これらのネイティブ要素に対して、誤った操作を行ったり、存在しないプロパティにアクセスしようとしたりすると、ネイティブクラッシュやJavaScriptエラーが発生する可能性があります。

原因

  • サードパーティライブラリが、getScrollableNode() で取得したノードとの互換性がない。
  • ネイティブモジュールとJavaScript間のブリッジ通信に問題がある。
  • 取得したネイティブノードの型を誤解している。

トラブルシューティング

  • デバッグツールの活用
    React Native DebuggerやXcode/Android Studioのデバッグツールを使って、ネイティブ側で何が起きているかを詳細に調査します。
  • 公式ドキュメントやライブラリのAPIを確認する
    getScrollableNode() で取得したノードに対して特定の操作を行いたい場合は、その操作がサポートされているか、またはそのノードがその操作に適しているかを、React Nativeの公式ドキュメントや関連ライブラリのAPIドキュメントで確認してください。
  • プラットフォームごとの差異を意識する
    開発環境(Web, iOS, Android)によって、getScrollableNode() が返すオブジェクトが異なることを理解し、それぞれのプラットフォームに合わせた処理を記述する必要があります。

パフォーマンスの問題

getScrollableNode() 自体が直接パフォーマンスの問題を引き起こすことは稀ですが、取得したノードに対して頻繁に低レベルな操作を行うと、パフォーマンスが低下する可能性があります。特に、スクロールイベントリスナーを直接追加し、イベントごとに重い処理を実行する場合などが該当します。

原因

  • スクロールイベントごとにDOM/ネイティブビューのプロパティを大量に読み書きする。
  • 頻繁な再レンダリングや、JavaScriptスレッドとネイティブスレッド間のブリッジ通信のオーバーヘッド。

トラブルシューティング

  • 必要最低限の操作に限定する
    getScrollableNode() を使用した低レベルな操作は、本当に必要な場合に限定し、React Nativeの高レベルなコンポーネントやAPIで実現できることはそちらを使用するようにします。
  • useNativeDriver の活用
    アニメーションを伴う場合は、可能な限り useNativeDriver: true を設定して、アニメーションをネイティブスレッドで実行させることで、JavaScriptスレッドの負荷を軽減します。
  • スロットリング/デバウンス
    スクロールイベントリスナーを追加する場合は、スロットリングやデバウンスを適用して、処理の実行頻度を制限します。

バージョンの非互換性

React Nativeのバージョンアップにより、内部実装が変更され、getScrollableNode() が返すオブジェクトの構造や挙動が変わる可能性があります。

原因

  • 新しいバージョンのReact Nativeの変更に追従できていない。
  • 古いバージョンのReact Nativeをターゲットに書かれたコードを、新しいバージョンで実行した場合。

トラブルシューティング

  • テスト
    バージョンアップ後は、既存の機能が正しく動作するか、特にgetScrollableNode()を使用している箇所について十分なテストを行います。
  • 公式リリースノートの確認
    React Nativeのバージョンアップを行う際は、公式のリリースノートやアップグレードガイドを必ず確認し、非推奨になったAPIや変更点がないかを確認します。
  • コミュニティの活用
    Stack OverflowやGitHub Issuesで同様の問題が報告されていないか検索します。
  • ログ出力とデバッグ
    console.log() を多用して、各変数の値やコードの実行パスを確認します。React Native DebuggerやVS Codeのデバッグ機能も活用します。
  • シンプルな例で再現する
    問題が発生した場合は、問題の箇所を切り出して、できるだけシンプルなコンポーネントで再現できるか試みます。


注意点
getScrollableNode() はReact Nativeの内部実装に依存するため、将来のバージョンで挙動が変わる可能性があります。通常はFlatListが提供するscrollToIndexscrollToOffsetなどのAPIを使用することを推奨します。ここに示す例は、あくまでも特定の高度なユースケースを想定しています。

例1: スクロールイベントを直接リッスンする(ネイティブ側)

React NativeのFlatListonScrollプロパティを提供していますが、これは最適化されており、全てのネイティブスクロールイベントをJavaScriptスレッドに渡すわけではありません。より詳細なスクロールイベントや、ネイティブ側のイベントリスナーにアクセスしたい場合にgetScrollableNode()が役立つことがあります。

前提
この例は、Web環境(React Native for Web)でDOMノードのイベントリスナーを追加するケースを想定しています。ネイティブアプリの場合、iOS/Androidそれぞれのネイティブモジュール連携が必要となり、より複雑になります。

import React, { useRef, useEffect, useState } from 'react';
import { FlatList, View, Text, StyleSheet, Platform, Button } from 'react-native';

const DATA = Array.from({ length: 50 }, (_, i) => ({ id: String(i), title: `アイテム ${i + 1}` }));

const DirectScrollEventExample = () => {
  const flatListRef = useRef(null);
  const [scrollOffset, setScrollOffset] = useState(0);

  useEffect(() => {
    // コンポーネントがマウントされた後にスクロールノードにアクセス
    if (flatListRef.current) {
      // getScrollableNode() で内部のスクロール可能な要素を取得
      const scrollableNode = flatListRef.current.getScrollableNode();

      if (scrollableNode) {
        // Platform.OS === 'web' の場合、これはHTMLのDOM要素になる
        // それ以外のネイティブ環境では、これはネイティブビューへの参照になる
        // ネイティブビューの場合、addEventListenerのようなDOM APIは使えないため、
        // ネイティブモジュール連携が必要になります。

        if (Platform.OS === 'web') {
          console.log("Web環境: DOM要素にイベントリスナーを追加します。");
          // Webの場合、DOM要素に直接 'scroll' イベントリスナーを追加
          const handleWebScroll = (event) => {
            setScrollOffset(event.target.scrollTop);
          };
          scrollableNode.addEventListener('scroll', handleWebScroll);

          // クリーンアップ関数でイベントリスナーを削除
          return () => {
            scrollableNode.removeEventListener('scroll', handleWebScroll);
          };
        } else {
          console.log("ネイティブ環境: getScrollableNode() はネイティブビューを返します。");
          console.log("ネイティブイベントリスナーを追加するには、ネイティブモジュールの連携が必要です。");
          // ネイティブ環境の場合(iOS/Android):
          // ここで取得できるのはネイティブビューへの参照です。
          // 例えば、Androidの場合は `android.widget.ScrollView` のインスタンス、
          // iOSの場合は `UIScrollView` のインスタンスに相当します。
          // これらに対して直接JavaScriptからイベントリスナーを追加することはできません。
          // ネイティブモジュールを介してカスタムイベントを購読するなどの対応が必要です。
          // 例:
          // const UIManager = require('react-native').UIManager;
          // UIManager.sendViewCommand(
          //   UIManager.findNodeHandle(scrollableNode),
          //   'setNativeScrollEventListener', // ←これは架空のコマンドです
          //   []
          // );
        }
      }
    }
  }, []); // 空の依存配列でマウント時のみ実行

  const scrollToTop = () => {
    if (flatListRef.current) {
      flatListRef.current.scrollToOffset({ offset: 0, animated: true });
    }
  };

  return (
    <View style={styles.container}>
      <Text style={styles.header}>スクロールオフセット (Web): {scrollOffset.toFixed(0)}</Text>
      <Button title="一番上へスクロール" onPress={scrollToTop} />
      <FlatList
        ref={flatListRef}
        data={DATA}
        renderItem={({ item }) => (
          <View style={styles.item}>
            <Text style={styles.title}>{item.title}</Text>
          </View>
        )}
        keyExtractor={(item) => item.id}
        style={styles.flatList}
      />
    </View>
  );
};

const styles = StyleSheet.create({
  container: {
    flex: 1,
    paddingTop: 50,
  },
  header: {
    fontSize: 18,
    fontWeight: 'bold',
    textAlign: 'center',
    marginBottom: 10,
  },
  flatList: {
    flex: 1,
  },
  item: {
    backgroundColor: '#f9c2ff',
    padding: 20,
    marginVertical: 8,
    marginHorizontal: 16,
    borderRadius: 8,
  },
  title: {
    fontSize: 24,
  },
});

export default DirectScrollEventExample;

解説

  • Platform.OS'ios' または 'android' の場合、getScrollableNode() はネイティブビューへの参照(オブジェクト)を返します。このオブジェクトに対しては、DOM API(addEventListenerなど)は直接使えません。ネイティブのスクロールイベントをJavaScriptに伝えるには、ネイティブモジュールを介したブリッジングが必要になります。これは、React Nativeのより高度なトピックです。
  • Platform.OS === 'web' の場合、返されるのは実際のHTMLのdiv要素のようなDOMノードです。これに対しては、標準のaddEventListenerを使ってスクロールイベントをリッスンできます。
  • useEffect フック内で FlatList がマウントされた後に getScrollableNode() を呼び出しています。

特定のネイティブUIライブラリが、React Nativeのコンポーネントが内部的に使用しているスクロールビューのインスタンスを必要とする場合があります。このような時、getScrollableNode() でそのインスタンスを取得し、ネイティブモジュールを介してライブラリに渡すことが考えられます。

これは具体的なコード例を示すのが難しいため、概念的な説明に留めます。

// 仮定: ネイティブモジュール MyNativeScrollManager が存在し、
// ネイティブのスクロールビューインスタンスを受け取って何か操作を行う
import { NativeModules, findNodeHandle } from 'react-native';
const { MyNativeScrollManager } = NativeModules;

// ... FlatList コンポーネントがあるとして

const flatListRef = useRef(null);

useEffect(() => {
  if (flatListRef.current) {
    const scrollableNode = flatListRef.current.getScrollableNode();

    // ネイティブモジュールにネイティブビューの参照を渡す
    // findNodeHandle は、React Nativeのコンポーネントまたはネイティブビューへの
    // 参照から、そのネイティブビューのID(タグ)を取得します。
    // ネイティブモジュールはこのIDを使ってネイティブビューにアクセスします。
    const nativeViewTag = findNodeHandle(scrollableNode);

    if (nativeViewTag) {
      // MyNativeScrollManager.registerScrollView(nativeViewTag);
      console.log(`ネイティブビューのタグ: ${nativeViewTag}`);
      console.log("このタグをネイティブモジュールに渡して操作できます。");
      // 例: MyNativeScrollManager.doSomethingWithScrollView(nativeViewTag);
    }
  }
}, []);

解説

  • ネイティブモジュールは、このタグを受け取ることで、そのタグに対応するネイティブビューインスタンスを取得し、直接操作することができます。
  • findNodeHandle(scrollableNode) は、getScrollableNode() から得られたネイティブビューへの参照(JavaScriptオブジェクト)を、そのネイティブビューの**タグ(ID)**に変換します。このタグは、ネイティブ側でそのビューを識別するために使用されます。

この種のプログラミングは、React Nativeのブリッジングメカニズム、つまりJavaScriptとネイティブコード間の通信を深く理解している必要があります。 非常に特定のニーズがない限り、推奨されるアプローチではありません。

FlatList.getScrollableNode() は、React Nativeの抽象化レイヤーを一時的に破り、基盤となるネイティブスクロールビューまたはDOM要素に直接アクセスすることを可能にします。これにより、React Nativeの標準APIでは不可能な、非常に低レベルな操作が可能になります。

しかし、その強力さゆえに、以下のようなデメリットも伴います。

  • 複雑性の増加
    JavaScriptとネイティブコード間のブリッジングや、ネイティブUIフレームワークの知識が必要になることがあります。
  • 将来の互換性
    React Nativeの内部実装変更により、このメソッドの挙動や返されるオブジェクトの構造が予告なく変わる可能性があります。
  • プラットフォームごとの差異
    iOS、Android、Webで返されるオブジェクトの型や操作方法が異なります。


しかし、ほとんどのシナリオでは、より安定しており、プラットフォームの差異を吸収してくれる以下の代替方法を検討すべきです。

FlatList 自身の Props やメソッドを使用する

最も推奨されるアプローチです。FlatList は豊富な props やメソッドを提供しており、スクロール関連のほとんどの操作はこれらで実現できます。

スクロール位置の制御

  • scrollWithoutAnimationTo(offset: number): アニメーションなしで指定したオフセットまでスクロールします。
    • 用途: アニメーションなしで即座に移動したい場合。
  • scrollToItem(params: { animated?: boolean; item: Item; viewOffset?: number; viewPosition?: number; }): 指定したアイテム(データオブジェクト)が表示されるようにスクロールします。keyExtractor が正しく設定されている必要があります。
    • 用途: scrollToIndex と同様ですが、インデックスではなくデータオブジェクト自体で指定したい場合。
  • scrollToIndex(params: { animated?: boolean; index: number; viewOffset?: number; viewPosition?: number; }): 指定したインデックスのアイテムが表示されるようにスクロールします。
    • 用途: 特定のアイテムへ移動したい場合(例: チャットアプリで最新メッセージへ、通知リストで未読アイテムへ)。
  • scrollToOffset(params: { animated?: boolean; offset: number; }): 指定したオフセット(ピクセル単位)までスクロールします。
    • 用途: 特定のピクセル位置へ移動したい場合。


import React, { useRef } from 'react';
import { FlatList, View, Text, Button, StyleSheet } from 'react-native';

const DATA = Array.from({ length: 50 }, (_, i) => ({ id: String(i), title: `アイテム ${i + 1}` }));

function FlatListControlExample() {
  const flatListRef = useRef(null);

  const scrollToMiddle = () => {
    if (flatListRef.current) {
      // 例: 25番目のアイテムへスクロール
      flatListRef.current.scrollToIndex({ index: 24, animated: true });
    }
  };

  const scrollToBottom = () => {
    if (flatListRef.current) {
      // コンテンツの最後までスクロール
      flatListRef.current.scrollToEnd({ animated: true });
    }
  };

  const scrollToTop = () => {
    if (flatListRef.current) {
      // 一番上までスクロール
      flatListRef.current.scrollToOffset({ offset: 0, animated: true });
    }
  };

  return (
    <View style={styles.container}>
      <View style={styles.buttonContainer}>
        <Button title="真ん中へ" onPress={scrollToMiddle} />
        <Button title="一番下へ" onPress={scrollToBottom} />
        <Button title="一番上へ" onPress={scrollToTop} />
      </View>
      <FlatList
        ref={flatListRef}
        data={DATA}
        renderItem={({ item }) => (
          <View style={styles.item}>
            <Text style={styles.title}>{item.title}</Text>
          </View>
        )}
        keyExtractor={(item) => item.id}
        // 他の重要な props:
        // onScroll: スクロールイベントをリッスン (スクロール位置や方向など)
        // scrollEventThrottle: onScroll イベントの発火頻度を制御 (デフォルトは0、つまりフレームごとに発火)
      />
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    paddingTop: 50,
  },
  buttonContainer: {
    flexDirection: 'row',
    justifyContent: 'space-around',
    marginBottom: 10,
  },
  item: {
    backgroundColor: '#f0f0f0',
    padding: 20,
    marginVertical: 8,
    marginHorizontal: 16,
    borderRadius: 8,
  },
  title: {
    fontSize: 18,
  },
});

export default FlatListControlExample;

スクロールイベントのリッスン

  • scrollEventThrottle: onScroll イベントが発火する頻度をミリ秒単位で制御します。値を大きくすると発火頻度が減り、パフォーマンスが向上しますが、スクロールイベントの粒度は粗くなります。
    • 用途: パフォーマンスが重要な場合、適切な頻度でイベントを受け取りたい場合。
  • onScroll: スクロールイベントが発生したときに呼び出されます。イベントオブジェクトには、スクロール位置(contentOffset)やコンテンツサイズなどが含まれます。
    • 用途: スクロールに応じた UI の変更(例: ヘッダーの非表示/表示、スクロール進捗バー)。
import React, { useState } from 'react';
import { FlatList, View, Text, StyleSheet, Animated } from 'react-native';

const DATA = Array.from({ length: 50 }, (_, i) => ({ id: String(i), title: `アイテム ${i + 1}` }));

function ScrollEventExample() {
  const scrollY = useRef(new Animated.Value(0)).current; // スクロール位置を追跡するためのAnimated.Value

  const handleScroll = Animated.event(
    [{ nativeEvent: { contentOffset: { y: scrollY } } }],
    { useNativeDriver: false } // onScroll イベントの Animated.Value マッピングでは useNativeDriver は false にすることが多い
  );

  // スクロール位置に基づいてヘッダーの不透明度を調整
  const headerOpacity = scrollY.interpolate({
    inputRange: [0, 100], // スクロールオフセットが0から100の範囲で
    outputRange: [1, 0],  // 不透明度が1から0に変化
    extrapolate: 'clamp', // 範囲外の値をクランプ
  });

  return (
    <View style={styles.container}>
      <Animated.View style={[styles.header, { opacity: headerOpacity }]}>
        <Text style={styles.headerText}>スクロールで消えるヘッダー</Text>
      </Animated.View>
      <FlatList
        data={DATA}
        renderItem={({ item }) => (
          <View style={styles.item}>
            <Text style={styles.title}>{item.title}</Text>
          </View>
        )}
        keyExtractor={(item) => item.id}
        onScroll={handleScroll} // Animated.event を onScroll に渡す
        scrollEventThrottle={16} // イベント発火頻度を制御 (16ms = 約60FPS)
      />
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    paddingTop: 0, // ヘッダーが上部に配置されるため
  },
  header: {
    position: 'absolute', // 相対的な位置にするためabsolute
    top: 0,
    left: 0,
    right: 0,
    height: 80, // ヘッダーの高さ
    backgroundColor: 'lightblue',
    justifyContent: 'center',
    alignItems: 'center',
    zIndex: 1, // アイテムの上に表示されるように
  },
  headerText: {
    fontSize: 20,
    fontWeight: 'bold',
  },
  item: {
    backgroundColor: '#f0f0f0',
    padding: 20,
    marginVertical: 8,
    marginHorizontal: 16,
    borderRadius: 8,
    marginTop: 80, // アイテムがヘッダーの下から始まるように調整
  },
  title: {
    fontSize: 18,
  },
});

export default ScrollEventExample;

onViewableItemsChanged を使用する

FlatListonViewableItemsChanged プロパティは、現在ビューポートに表示されているアイテムが変化したときに呼び出されます。

  • 用途: 無限スクロールの検知、表示されているアイテムに応じた分析イベントの送信、動画の自動再生/停止など。
import React, { useState, useCallback, useRef } from 'react';
import { FlatList, View, Text, StyleSheet } from 'react-native';

const DATA = Array.from({ length: 50 }, (_, i) => ({ id: String(i), title: `アイテム ${i + 1}` }));

function ViewabilityExample() {
  const [visibleItems, setVisibleItems] = useState([]);

  // onViewableItemsChanged が呼び出される際のビューアビリティ設定
  const viewabilityConfig = useRef({
    minimumViewTime: 300, // アイテムがビューポートに表示されてからこの時間(ms)が経過するまで onViewableItemsChanged は発火しない
    itemVisiblePercentThreshold: 50 // アイテムの50%以上が表示されたら「表示された」とみなす
  }).current;

  const onViewableItemsChanged = useCallback(({ viewableItems, changed }) => {
    // console.log("Viewable Items:", viewableItems);
    // console.log("Changed Items:", changed);
    setVisibleItems(viewableItems.map(item => item.item.title));
  }, []);

  return (
    <View style={styles.container}>
      <Text style={styles.header}>現在表示中のアイテム:</Text>
      <Text style={styles.visibleItemsText}>
        {visibleItems.length > 0 ? visibleItems.join(', ') : 'なし'}
      </Text>
      <FlatList
        data={DATA}
        renderItem={({ item }) => (
          <View style={styles.item}>
            <Text style={styles.title}>{item.title}</Text>
          </View>
        )}
        keyExtractor={(item) => item.id}
        onViewableItemsChanged={onViewableItemsChanged}
        viewabilityConfig={viewabilityConfig}
      />
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    paddingTop: 50,
  },
  header: {
    fontSize: 18,
    fontWeight: 'bold',
    textAlign: 'center',
    marginBottom: 10,
  },
  visibleItemsText: {
    textAlign: 'center',
    marginBottom: 20,
    color: 'blue',
  },
  item: {
    backgroundColor: '#d0f0d0',
    padding: 20,
    marginVertical: 8,
    marginHorizontal: 16,
    borderRadius: 8,
  },
  title: {
    fontSize: 18,
  },
});

export default ViewabilityExample;

ScrollView への直接アクセス(FlatList の場合ではないが関連)

FlatList が内部的に ScrollView を使用していることは事実ですが、FlatList は仮想化リストのロジックをラップしているため、直接 ScrollView のプロパティやメソッドにアクセスすることは通常ありません。

しかし、もし単にスクロール可能なビューが必要で、FlatList の仮想化機能が不要な場合は、直接 ScrollView コンポーネントを使用し、その ref を介して scrollToscrollResponderScrollTo メソッドにアクセスできます。

ScrollView のメソッド

  • scrollResponderScrollWithoutAnimationTo(x: number, y: number): アニメーションなしでスクロール。
  • scrollResponderScrollTo(options: { x?: number; y?: number; animated?: boolean; }): 同上。
  • scrollTo(options: { x?: number; y?: number; animated?: boolean; }): 指定した x, y 座標までスクロールします。

上記の代替方法で対応できない、以下のような非常に特殊な状況でのみ getScrollableNode() を検討します。

  1. 非常に低レベルなネイティブのスクロールイベントリスナーを登録したい場合: onScroll イベントが提供する情報や頻度では不十分で、ネイティブの UIScrollViewDelegateOnScrollChangeListener と同等のレベルでイベントを処理したい場合。ただし、これにはネイティブモジュールを記述し、JavaScript とブリッジングする必要があります。

  2. 既存のネイティブライブラリが、特定のネイティブスクロールビューインスタンスを直接必要とする場合: React Native には対応するコンポーネントがなく、かつネイティブライブラリが UIScrollViewandroid.widget.ScrollView のような特定のインスタンスを引数として受け取る API を持っている場合。これもネイティブモジュールの実装が必要です。

  3. React Native Web で、生の DOM 要素へのアクセスが必要な場合: Web 標準の API (element.scrollIntoView(), element.addEventListener()) を使って、FlatList がレンダーするスクロールコンテナの DOM 要素に直接アクセスしたい場合。