关于 gesture-handler-3-migration
gesture-handler-3-migration 是用于迁移 React Native 代码的编码 skill,将 react-native-gesture-handler 的构建器 API 更新为 Gesture Handler 3 的 hook API。它涵盖手势关系、回调、组件和相关类型,并指导处理需保留旧版组件或先向用户确认的情况。
主要功能
- 01将构建器调用替换为手势 hook
- 02将构建器链改为配置对象
- 03更新回调与事件类型
- 04迁移组合及跨组件关系
- 05处理 SVG 检测器层级变化
- 06指导旧版组件与 Touchable 迁移
适用场景
- 01将 React Native 应用升级到 Gesture Handler 3
- 02迁移基于构建器的手势定义
- 03更新回调与手势关系
- 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。
- 找出所有从 'react-native-gesture-handler' 导入的内容
- 对每个
Gesture.X()调用,将其替换为对应的useXGesture()Hook - 将
Gesture导入替换为所用 Hook 的导入 - 将构建器方法链转换为配置对象
- 更新回调名称(onStart → onActivate 等)
- 将组合手势替换为关系 Hook。请注意遵守 Hook 规则
- 如果涉及 SVG,请将 GestureDetector 用法更新为 Intercepting/Virtual GestureDetector
- 根据“Legacy components”部分更新从 'react-native-gesture-handler' 导入的 compoenent 用法
迁移手势
所有 Hook 手势在构建器 API 中都有对应项:Gesture.X() 会变为 useXGesture(config)。除非另有说明,方法现在是配置对象中与相关构建器方法同名的字段。
例外是 Gesture.ForceTouch,它在 Hook API 中没有对应项。
回调变更
在 Gesture Handler 3 中,部分回调更名如下:
onStart->onActivateonEnd->onDeactivateonTouchesCancelled->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()->PanGestureEventuseTapGesture()->TapGestureEventuseLongPressGesture()->LongPressGestureEventuseRotationGesture()->RotationGestureEventusePinchGesture()->PinchGestureEventuseFlingGesture()->FlingGestureEventuseHoverGesture()->HoverGestureEventuseNativeGesture()->RotationGestureEventuseManualGesture()->ManualGestureEvent
触摸事件除外:
onTouchesDownonTouchesUponTouchesMoveonTouchesCancel
无论使用哪个 Hook,每个回调都会接收 GestureTouchEvent。
StateManager
在 Gesture Handler 3 中,stateManager 不再传递给 TouchEvent 回调。取而代之的是,应使用全局的 GestureStateManager。
GestureStateManager 提供用于命令式状态管理的方法:
- .activate(handlerTag: number)
- .deactivate(handlerTag: number)(旧 API 中的 .end())
- .fail(handlerTag: number)
handlerTag 可通过以下两种方式获取:
- 从 Hook 返回的手势对象获取(
gesture.handlerTag) - 从回调中的事件获取(
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— 取代旧版enabledprop(注意含义相反)。默认值为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。