なつねこメモ

主にプログラミング関連のメモ帳 ♪(✿╹ヮ╹)ノ 書いてあるコードは自己責任でご自由にどうぞ。記事本文の無断転載は禁止です。

React Native でボトムタブナビゲーションをタップしたら上へスクロールする、をやりたい

Twitter アプリなどでよく見かける、アクティブなタブを再度タップしたら、リストビューの最上部へ移動する......といった挙動を React Native で実装したい!というときのやり方。 タブナビゲーションに React Nativation / Expo Router を使っている場合は、 useScrollToTop フックを使うことで、簡単に実現できます。

まずは導入から、 pnpm を使っている場合は次のコマンドで、React Navigation を明示的に導入する:

$ pnpm add @react-navigation/native

次に、ボトムタブを実装する。おおよそ次のようなコード例になるはず。

// @app/_layout.tsx
import { Tabs } from "expo-router";

export default function TabLayout() {
  return (
    <Tabs
      screenOptions={{
        headerShown: false,
      }}
    >
      <Tabs.Screen name="index" />
      <Tabs.Screen name="explore" />
      <Tabs.Screen name="notifications" />
      <Tabs.Screen name="profile" />
    </Tabs>
  );
}

次に、実際に useScrollToTop を使う。 useScrollToTop には特定のインターフェースを満たすオブジェクトを渡す必要があるが、今回は scrollToTop だけを実装したものを渡す。 タブの中身と一緒に実装した例だと、次のような形になるはず。

// @app/explore.tsx

import { useScrollToTop } from "@react-navigation/native";
import { FlashList, FlashListRef } from "@shopify/flash-list";
import React, { useMemo, useRef } from "react";
import { View } from "react-native";

export default function ExploreScreen() {
  const listRef= useRef<FlashListRef<any>>(null);
  const [items, ] = useState<{ id: string }[]>([]);
  const scrollerObj = useRef<{ scrollToTop: () => void }>(null);
  const scroller = useMemo(() => {
    return {
      scrollToTop: () => {
        listRef.current?.scrollToOffset({ offset: 0, animated: true }); // 最上部へスクロール
      }
    }
   }, [activeTab]);
  scrollerObj.current = scroller;

  useScrollToTop(scrollerObj);

  return (
    <FlashList
      ref={listRef}
      keyExtractor={(w) => w.id}
      data={items}
      // 略
    />
  );
}

これだけで Twitter アプリとかでよく見る「ボトムタブナビゲーションをタップしたら上へスクロールする」ができる。簡単。ということでメモでした。