mcpField
G
gesture-handler-3-migration

gesture-handler-3-migration

github.com/software-mansion開発6.8k73 回インストール今日更新
リポジトリ

gesture-handler-3-migration について

gesture-handler-3-migrationは、React Nativeコードをreact-native-gesture-handlerのビルダーベースAPIからGesture Handler 3のフックAPIへ移行するコーディングスキルです。ジェスチャーの関係、コールバック、コンポーネント、関連型を扱い、旧版コンポーネントが必要な場合やユーザー確認が必要な場合も案内します。

主な機能

  1. 01ビルダー呼び出しをジェスチャーフックに置換
  2. 02ビルダーチェーンを設定オブジェクトに変更
  3. 03コールバックとイベント型の更新
  4. 04合成およびコンポーネント間の関係を移行
  5. 05SVGの検出器階層変更に対応
  6. 06旧版コンポーネントとTouchableの移行案内

活用シーン

  1. 01React NativeアプリをGesture Handler 3へ更新
  2. 02ビルダーベースのジェスチャー定義を移行
  3. 03コールバックとジェスチャー関係を更新
  4. 04移行時の旧版コンポーネントを検討

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/software-mansion/react-native-gesture-handler --skill gesture-handler-3-migration

インストール後、Claude Code で/gesture-handler-3-migrationを入力して読み込まれたか確認します。

詳細

Gesture Handler 3 に移行する

このスキルは、Gesture Handler のビルダーベース API を使用する React Native コンポーネントを検出し、新しいフックベース API を使用するように更新します。また、新しいバージョンに対応するため、関連する型やコンポーネントも更新します。

使用する場面

  • react-native-gesture-handler からインポートしたコンポーネントの使用方法を更新するとき
  • Gesture Handler 3 にアップグレードするとき
  • 新しいフックベースのジェスチャー API に移行するとき

手順

以下の手順に従い、すべてのレガシー API を新しい API に正しく置き換えてください。

  1. 'react-native-gesture-handler' からのインポートをすべて特定します
  2. 各 Gesture.X() 呼び出しを、対応する useXGesture() フックに置き換えます
  3. Gesture のインポートを、使用するフックのインポートに置き換えます
  4. ビルダーメソッドチェーンを設定オブジェクトに変換します
  5. コールバック名を更新します(onStart → onActivate など)
  6. 合成ジェスチャーを関係フックに置き換えます。フックのルールを守ってください
  7. SVG が関係する場合は、GestureDetector の使用を Intercepting/Virtual GestureDetector に更新します
  8. 「レガシーコンポーネント」セクションに従って、react-native-gesture-handler からインポートしたコンポーネントの使用方法を更新します
ジェスチャーの移行

すべてのフックジェスチャーには、ビルダー API の対応するものがあります。Gesture.X() は useXGesture(config) になります。特に指定がない限り、メソッドは対応するビルダーメソッドと同じ名前の設定オブジェクトのフィールドになりました。

例外は Gesture.ForceTouch で、フック API には対応するものがありません。

コールバックの変更

Gesture Handler 3 では、一部のコールバック名が次のように変更されました。

  • onStart -> onActivate
  • onEnd -> onDeactivate
  • onTouchesCancelled -> onTouchesCancel

onDeactivate および onFinalize コールバックは、2 番目の didSucceed/success 論理値パラメーターを受け取らなくなりました。代わりに、イベントオブジェクトに canceled プロパティが含まれます。ロジックが反転しており、canceled: true は以前の success: false に相当する点に注意してください。

// Old (RNGH2)
.onEnd((event, success) => {
  if (success) { /* gesture succeeded */ }
})

// New (RNGH3)
onDeactivate: (event) => {
  if (!event.canceled) { /* gesture succeeded */ }
}

フック API では onChange は使用できなくなりました。代わりに、*change* プロパティは onUpdate 内で使用できるイベントに移動しました。

ジェスチャーのすべてのコールバックで、同じ型を使用するようになりました。

  • usePanGesture() -> PanGestureEvent
  • useTapGesture() -> TapGestureEvent
  • useLongPressGesture() -> LongPressGestureEvent
  • useRotationGesture() -> RotationGestureEvent
  • usePinchGesture() -> PinchGestureEvent
  • useFlingGesture() -> FlingGestureEvent
  • useHoverGesture() -> HoverGestureEvent
  • useNativeGesture() -> RotationGestureEvent
  • useManualGesture() -> ManualGestureEvent

例外はタッチイベントです。

  • onTouchesDown
  • onTouchesUp
  • onTouchesMove
  • onTouchesCancel

どのフックを使用しているかにかかわらず、各コールバックは GestureTouchEvent を受け取ります。

StateManager

Gesture Handler 3 では、stateManager は TouchEvent コールバックに渡されなくなりました。代わりに、グローバルな GestureStateManager を使用してください。

GestureStateManager は、命令的な状態管理のためのメソッドを提供します。

  • .activate(handlerTag: number)
  • .deactivate(handlerTag: number)(旧 API の .end())
  • .fail(handlerTag: number)

handlerTag は、次の 2 つの方法で取得できます。

  1. フックから返されるジェスチャーオブジェクトから(gesture.handlerTag)
  2. コールバック内のイベントから(event.handlerTag)

コールバック定義から、定義中のジェスチャーを参照することはできません。この場合は、イベントを使用してハンドラータグを取得してください。

ジェスチャーは GestureStateManager でアクティブ化する前に、タッチイベントによって自動的に BEGAN 状態に移行する必要があるため、GestureStateManager.begin() は削除してください。

関係の移行
合成ジェスチャー

Gesture.Simultaneous(gesture1, gesture2); は useSimultaneousGestures(pan1, pan2); になります。

旧 API のすべての関係と、新 API での対応するものは次のとおりです。

  • Gesture.Race() -> useCompetingGestures()
  • Gesture.Simultaneous() -> useSimultaneousGestures()
  • Gesture.Exclusive() -> useExclusiveGestures()
コンポーネント間の関係プロパティ

コンポーネント間のインタラクションを定義するために使用されるプロパティの名前が変更されました。

  • .simultaneousWithExternalGesture -> simultaneousWith:
  • .requireExternalGestureToFail -> requireToFail:
  • .blocksExternalGesture -> block:
GestureDetector

GestureDetector は react-native-gesture-handler の主要なコンポーネントです。フック API またはビルダーパターンを使って作成したジェスチャーをサポートします。ただし、両方を混在させることはできず、どちらか一方を使う必要があります。

同じジェスチャーのインスタンスを複数の Gesture Detector で使わないでください。未定義の動作が発生する可能性があります。

Reanimated との統合

Worklets の Babel プラグインは、設定チェーン内でジェスチャーに渡されるコールバックを自動的に worklet としてマークするように設定されています。そのため、関数の先頭に 'worklet'; ディレクティブを追加する必要はありません。

コールバックがジェスチャーオブジェクトの外部で定義されている場合、worklet 化されません。

const callback = () => {
  console.log(_WORKLET);
};

const gesture = useTapGesture({
  onBegin: callback,
});

他の高階関数でラップされたコールバックは worklet 化されません。

const gesture = useTapGesture({
  onBegin: useCallback(() => {
    console.log(_WORKLET);
  }, []),
});

上記の場合は、コールバックの最初の行に "worklet"; ディレクティブを追加してください。

Reanimated の無効化

フック API で作成されたジェスチャーでは、Reanimated との統合がデフォルトで有効になっています(インストールされている場合)。つまり、すべてのコールバックは UI スレッドで実行されます。

runOnJS

runOnJS プロパティを使用すると、コールバックを JS スレッドと UI スレッドのどちらで実行するかを動的に制御できます。true に設定すると、コールバックは JS スレッドで実行されます。false に設定すると、UI スレッドで実行されます。デフォルト値は false です。

ビューハイアラルキーに依存するコンポーネントの移行

SVG などの一部のコンポーネントは、正しく機能するためにビューハイアラルキーに依存します。Gesture Handler 3 では、GestureDetector がこの階層を乱します。この問題を解決するために、InterceptingGestureDetector と VirtualGestureDetector の 2 つの新しい検出器が導入されました。

InterceptingGestureDetector は GestureDetector と同様に機能しますが、コンポーネントサブツリー内で VirtualGestureDetector のプロキシとしても機能します。仮想検出器のコンテキストを確立するためだけに使用することもできるため、gesture プロパティは省略可能です。

VirtualGestureDetector は RNGH2 の GestureDetector に似ています。ホストコンポーネントではないため、ホストビューの階層に干渉しません。これにより、その階層に依存する機能を妨げることなく、ジェスチャーをアタッチできます。

警告: VirtualGestureDetector は InterceptingGestureDetector の子孫である必要があります。

SVG の移行

Gesture Handler 2 では、GestureDetector を SVG に直接使用できました。Gesture Handler 3 で SVG を扱う正しい方法は、InterceptingGestureDetector と VirtualGestureDetector を使用することです。

レガシーコンポーネント

コンポーネントを使用するコードが、Gesture Handler 3 のコンポーネントでは利用できなくなった API(waitFor、simultaneousWith、blocksHandler、onHandlerStateChange、onGestureEvent プロパティなど)に依存している場合、そのコンポーネントだけを切り離して簡単に移行することはできません。この場合は、インポートを Legacy 版のコンポーネントに変更し、先に依存関係を移行する必要があることをユーザーに伝えてください。

移行が可能な場合は、ユーザーの意図が事前にはっきり示されていない限り、質問ツールを使って確認してください。インポート時に Legacy 接頭辞のない新しい実装を使用するのか、それともインポート時に Legacy 接頭辞を付けて旧実装に戻すのかを確認します。

Gesture Handler のボタンを React Native のコンポーネントに置き換えたり、その逆を提案したりしないでください。

ボタンの実装は更新され、ボタン関連の問題の大半が解決されました。また、内部的に新しいフック API を使うよう書き換えられています。ボタンコンポーネントのレガシーな JS 実装も引き続き利用できますが、Legacy 接頭辞が付いた名前に変更されています。たとえば、RectButton は LegacyRectButton として利用できるようになりました。これらも内部では新しいネイティブコンポーネントを使用します。

PureNativeButton は削除されました。見つかった場合は、削除されたことをユーザーに伝え、対応方法を判断してもらってください。他のボタンを使って同様の機能を実現できます。

ReanimatedSwipeable の dragOffsetFromRight プロパティは、負の値を受け付けるようになりました。正の値で使用していた場合は、符号を反転してください。

他のコンポーネントも内部的に新しいフック API を使うよう書き換えられていますが、従来の名前でエクスポートされているため、変更は必要ありません。ただし、何らかの理由で以前の実装を使う必要がある場合は、Legacy 接頭辞が付いたレガシーコンポーネントも利用できます。たとえば、ScrollView は LegacyScrollView として利用できるようになりました。

createNativeWrapper のすべての箇所を legacy_createNativeWrapper に変更してください。インポート文と関数呼び出しの両方が対象です。

Touchable への移行

Gesture Handler 3 では、Touchable コンポーネントが従来のボタン(BaseButton、RectButton、BorderlessButton)と、従来のコアスタイルのタッチャブル(TouchableOpacity、TouchableHighlight、TouchableWithoutFeedback、TouchableNativeFeedback)の両方に代わります。これは、見た目のフィードバックをすべて props で制御する単一のコンポーネントです。別のコンポーネントを選ぶのではなく、適切な組み合わせを選んでください。

移行時に使用する props:

  • onPress(event) — タップが成功したときに呼び出されます。注:コールバックのシグネチャが変更されました。以前の BaseButton.onPress が (pointerInside: boolean) を受け取っていたのに対し、Touchable.onPress はジェスチャーイベントオブジェクトを受け取ります。
  • onPressIn(event) / onPressOut(event) — ポインターが最初に触れたとき、およびコンポーネントから離れるかリリースされたときに呼び出されます。
  • onLongPress() — 押下が delayLongPress ミリ秒(デフォルトは 600)続いた後に呼び出されます。長押しが発生した場合、その後のリリースでは onPress は呼び出されません。
  • disabled — 以前の enabled prop に代わります(意味が反転している点に注意してください)。デフォルトは false です。
  • cancelOnLeave — ポインターがコンポーネントの境界から出たときに押下をキャンセルするかどうかを指定します。デフォルトは true です。従来のボタンの shouldCancelWhenOutside の代わりに使用してください。
  • activeOpacity — 押下中にコンポーネント自体に適用される不透明度です(TouchableOpacity と同様)。デフォルトは 1(不透明度の変化なし)です。
  • underlayColor + activeUnderlayOpacity — 押下中に表示される下地の色と不透明度です(TouchableHighlight / RectButton と同様)。underlayColor のデフォルトは 'transparent'、activeUnderlayOpacity のデフォルトは 0.105 です。
  • androidRipple — Android のリップル設定です({ color?, radius?, borderless?, foreground? })。省略した場合、ネイティブのリップルは描画されません。TouchableNativeFeedback の代わりに使用してください。
  • animationDuration — 押下/ホバーのアニメーション時間をミリ秒で指定します。単一の数値を渡すとすべてのフェーズに適用されます。または、{ in, out }(必要に応じて tap/hover/longPress のオーバーライドを指定可能)を渡します。デフォルトは in が 50、out が 100 です。
  • hitSlop、testID、style、children — 以前と同じです。
Gesture Handler のボタンを置き換える
以前のコンポーネント置き換え先(iOS/クロスプラットフォームのデフォルト)
BaseButton<Touchable />(デフォルトの props)
RectButton<Touchable underlayColor="black" animationDuration={0} />
BorderlessButton<Touchable activeOpacity={0.3} animationDuration={0} />

Android のリップル: 従来の RectButton/BorderlessButton は Android でネイティブのテーマリップルを使用しますが、Touchable は androidRipple が設定されていない限りリップルを無効にします。従来の Android のフィードバックを維持するには、Android では underlayColor/activeOpacity/animationDuration の代わりに androidRipple を設定してください(これらを組み合わせないでください。Android ではリップルが視覚的なフィードバックになります)。設定は次のように異なります。

  • RectButton → androidRipple={{}}
  • BorderlessButton → androidRipple={{ borderless: true }}(従来のボーダーレスリップルの形状に一致します)

プラットフォームごとに異なる props を適用するには Platform.select を使用します。RectButton の例:

import { Platform } from 'react-native';

<Touchable
  {...Platform.select({
    android: { androidRipple: {} },
    default: { underlayColor: 'black', animationDuration: 0 },
  })}
/>
旧式の Touchable を置き換える
旧コンポーネント置き換え先
TouchableOpacity<Touchable activeOpacity={0.2} animationDuration={{ in: 0, out: 150 }} />
TouchableHighlight<Touchable underlayColor={...} activeUnderlayOpacity={1} activeOpacity={...} /> — 最も近い代替手段のみです(完全な置き換えではありません。下記の注記を参照してください)
TouchableWithoutFeedback<Touchable />(プレーンな状態。視覚的フィードバック用の props はありません)
TouchableNativeFeedback<Touchable androidRipple={{ foreground: true }} />(旧コンポーネントのデフォルトではリップルが前景に描画されます。元のコードで useForeground={false} が渡されていた場合は、foreground を削除してください)

TouchableNativeFeedback では、androidRipple を明示的に設定する必要があります。設定しないとリップルは描画されません。旧コンポーネントのデフォルトは useForeground: true なので、{ foreground: true } が最も近いデフォルトの置き換え先です。元のコードで useForeground={false} が設定されていた場合に限り、foreground を省略してください。元のコードで background prop をカスタマイズしていた場合は、color、radius、または borderless を追加してください。

TouchableHighlight は、完全に 1:1 で置き換えることはできません。旧コンポーネントでは、コンテナ自体の背景がアンダーレイ(単色の underlayColor)になり、activeOpacity はその上の子要素だけを暗くするため、暗くなった子要素の下からアンダーレイが見えます。一方、Touchable では、背景と子要素の間に独立したアンダーレイレイヤーがあり、activeOpacity はコンポーネント全体(背景、アンダーレイ、子要素すべて)をまとめて暗くします。最も近い代替方法は、underlayColor と activeOpacity をそのまま引き継ぎ、アンダーレイレイヤーを単色で表示するために activeUnderlayOpacity={1} を追加することです。レイヤー構造の違いにより、旧コンポーネントとは視覚的フィードバックが異なる場合があることをユーザーに伝えてください。

移行中に、Gesture Handler のボタンや Touchable を React Native コアコンポーネントに置き換えたり、その逆を行ったりしないでください。react-native-gesture-handler 内で統一してください。

置き換えられた型

TapGesture など、ビルダー API で使われる型の多くは Gesture Handler 3 に引き続き存在します。ただし、現在は新しいフック API で使われます。ビルダー API の型には Legacy という接頭辞が付くようになりました。たとえば、TapGesture は LegacyTapGesture になります。

コメント