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 代码的编码 skill,将 react-native-gesture-handler 的构建器 API 更新为 Gesture Handler 3 的 hook API。它涵盖手势关系、回调、组件和相关类型,并指导处理需保留旧版组件或先向用户确认的情况。

主要功能

  1. 01将构建器调用替换为手势 hook
  2. 02将构建器链改为配置对象
  3. 03更新回调与事件类型
  4. 04迁移组合及跨组件关系
  5. 05处理 SVG 检测器层级变化
  6. 06指导旧版组件与 Touchable 迁移

适用场景

  1. 01将 React 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 组件,并将其更新为使用新的基于 Hook 的 API。它还会更新相关类型和组件,以适配新版本。

使用场景

  • 更新从 react-native-gesture-handler 导入的组件用法
  • 升级到 Gesture Handler 3
  • 迁移到新的基于 Hook 的手势 API

说明

请按照以下说明,将所有旧版 API 正确替换为新版 API。

  1. 找出所有从 'react-native-gesture-handler' 导入的内容
  2. 对每个 Gesture.X() 调用,将其替换为对应的 useXGesture() Hook
  3. 将 Gesture 导入替换为所用 Hook 的导入
  4. 将构建器方法链转换为配置对象
  5. 更新回调名称(onStart → onActivate 等)
  6. 将组合手势替换为关系 Hook。请注意遵守 Hook 规则
  7. 如果涉及 SVG,请将 GestureDetector 用法更新为 Intercepting/Virtual GestureDetector
  8. 根据“Legacy components”部分更新从 'react-native-gesture-handler' 导入的 compoenent 用法
迁移手势

所有 Hook 手势在构建器 API 中都有对应项:Gesture.X() 会变为 useXGesture(config)。除非另有说明,方法现在是配置对象中与相关构建器方法同名的字段。

例外是 Gesture.ForceTouch,它在 Hook API 中没有对应项。

回调变更

在 Gesture Handler 3 中,部分回调更名如下:

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

onDeactivate 和 onFinalize 回调不再接收第二个 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 */ }
}

在 Hook 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

无论使用哪个 Hook,每个回调都会接收 GestureTouchEvent。

StateManager

在 Gesture Handler 3 中,stateManager 不再传递给 TouchEvent 回调。取而代之的是,应使用全局的 GestureStateManager。

GestureStateManager 提供用于命令式状态管理的方法:

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

handlerTag 可通过以下两种方式获取:

  1. 从 Hook 返回的手势对象获取(gesture.handlerTag)
  2. 从回调中的事件获取(event.handlerTag)

回调定义不能引用正在定义的手势。在这种情况下,请通过事件获取 handler tag。

移除 GestureStateManager.begin(),因为手势现在必须先通过触摸事件自动进入 BEGAN 状态,之后才能通过 GestureStateManager 激活。

迁移关系
组合手势

Gesture.Simultaneous(gesture1, gesture2); 会变为 useSimultaneousGestures(pan1, pan2);

旧版 API 中的所有关系及其在新版中的对应项:

  • Gesture.Race() -> useCompetingGestures()
  • Gesture.Simultaneous() -> useSimultaneousGestures()
  • Gesture.Exclusive() -> useExclusiveGestures()
跨组件关系属性

用于定义跨组件交互的属性已重命名:

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

GestureDetector 是 react-native-gesture-handler 的核心组件。它支持使用钩子 API 或构建器模式创建的手势(但两种方式不能混用,只能选择其中一种)。

不要在多个 GestureDetector 之间使用同一个手势实例,否则会导致未定义行为。

与 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。

InterceptingGestureDetector 的功能与 GestureDetector 类似,但还可以作为其组件子树中 VirtualGestureDetector 的代理。由于它可以仅用于为虚拟检测器建立上下文,因此 gesture 属性是可选的。

VirtualGestureDetector 与 RNGH2 中的 GestureDetector 类似。由于它不是宿主组件,因此不会干扰宿主视图层级。这样你就可以附加手势,而不会影响依赖该层级的功能。

警告: VirtualGestureDetector 必须是 InterceptingGestureDetector 的后代。

迁移 SVG

在 Gesture Handler 2 中,可以直接在 SVG 上使用 GestureDetector。在 Gesture Handler 3 中,与 SVG 交互的正确方式是使用 InterceptingGestureDetector 和 VirtualGestureDetector。

旧版组件

如果使用该组件的代码依赖 Gesture Handler 3 中组件已不再提供的 API(例如 waitFor、simultaneousWith、blocksHandler、onHandlerStateChange、onGestureEvent 属性),就无法轻松地单独迁移该组件。在这种情况下,请将导入项更新为该组件的旧版,并告知用户需要先迁移这些依赖项。

如果可以迁移,除非用户已明确说明,否则请使用 ask questions 工具澄清用户的意图:组件应该使用新实现(导入时不带 Legacy 前缀),还是应该恢复为旧实现(导入时带 Legacy 前缀)?

不要建议将 Gesture Handler 中的按钮替换为 React Native 中的组件,反之亦然。

按钮的实现已更新,解决了大多数与按钮相关的问题。它们也已在内部重写,以使用新的 hook API。按钮组件的旧版 JS 实现仍可使用,但名称已添加 Legacy 前缀,例如,RectButton 现在可通过 LegacyRectButton 使用。它们底层仍使用新的原生组件。

PureNativeButton 已被移除。如果遇到它,请告知用户该组件已被移除,并让他们决定如何处理。其他按钮也能实现类似功能。

ReanimatedSwipeable 的 dragOffsetFromRight 属性现在接受负值。如果之前使用的是正值,请务必将其改为负值。

其他组件也已在内部重写,改为使用新的 hook API,但导出名称保持不变,因此你无需做任何更改。不过,如果出于任何原因需要使用旧版实现,也可以使用旧版组件,它们的名称带有 Legacy 前缀,例如,ScrollView 现在可通过 LegacyScrollView 使用。

将所有 createNativeWrapper 实例重命名为 legacy_createNativeWrapper。这包括导入语句和函数调用。

迁移到 Touchable

在 Gesture Handler 3 中,Touchable 组件取代了旧版按钮(BaseButton、RectButton、BorderlessButton)和旧式核心触摸组件(TouchableOpacity、TouchableHighlight、TouchableWithoutFeedback、TouchableNativeFeedback)。它是一个单一组件,视觉反馈完全由 props 控制——选择合适的 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 覆盖设置)。默认值为进入时 50 / 退出时 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 上设置 androidRipple,而不要设置 underlayColor/activeOpacity/animationDuration(不要将它们组合使用——在 Android 上,波纹就是视觉反馈)。两种配置不同:

  • RectButton → androidRipple={{}}
  • BorderlessButton → androidRipple={{ borderless: true }}(与旧版无边框波纹形状一致)

使用 Platform.select 按平台应用不同的 props。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={...} /> — 仅为最接近的近似方案(并非 1:1 替换,详见下方说明)
TouchableWithoutFeedback<Touchable />(普通用法,不带视觉反馈属性)
TouchableNativeFeedback<Touchable androidRipple={{ foreground: true }} />(旧版默认在前景绘制波纹;如果原代码传入了 useForeground={false},则移除 foreground)

对于 TouchableNativeFeedback,必须显式设置 androidRipple,否则不会绘制波纹。旧版组件默认使用 useForeground: true,因此 { foreground: true } 是最接近默认行为的替换方案;仅当原代码设置了 useForeground={false} 时,才省略 foreground。如果原代码自定义了 background 属性,请添加 color、radius 或 borderless。

对于 TouchableHighlight,无法实现完美的 1:1 替换——在旧版组件中,容器自身的背景会成为底色层(纯色 underlayColor),而 activeOpacity 只会降低上层子元素的不透明度,因此底色会透过变暗的子元素显示出来。Touchable 则在背景和子元素之间设有独立的底色层,并且其 activeOpacity 会降低整个组件(背景 + 底色层 + 子元素)的不透明度。最接近的近似方案是保持 underlayColor 和 activeOpacity 不变,并添加 activeUnderlayOpacity={1},使底色层保持不透明。请告知用户,由于图层结构不同,视觉反馈可能与旧版组件有所差异。

迁移期间,不要将 Gesture Handler 按钮或触摸组件替换为 React Native 核心组件,反之亦然——请继续使用 react-native-gesture-handler 中的组件。

替换的类型

构建器 API 中使用的大多数类型(例如 TapGesture)在 Gesture Handler 3 中仍然存在。不过,它们现在用于新的 hook API。构建器 API 的类型现在带有 Legacy 前缀,例如,TapGesture 变为 LegacyTapGesture。

评论

支持 Markdown