Skip to content

Commit bdf353d

Browse files
authored
Export new createNativeWrapper (#3971)
## Description This PR exports new `createNativeWrapper` implementation and renames old one to `legacy_createNativeWrapper`. It also updated documentation, along with skill for LLM migration. ## Test plan Check example from new docs.
1 parent 3f91ea5 commit bdf353d

File tree

5 files changed

+180
-4
lines changed

5 files changed

+180
-4
lines changed
Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
---
2+
id: create-native-wrapper
3+
title: createNativeWrapper
4+
sidebar_label: createNativeWrapper
5+
---
6+
7+
`createNativeWrapper` is a function that lets you wrap components which are not provided by Gesture Handler with a [`Native gesture`](/docs/gestures/use-native-gesture), allowing them to participate in the gesture recognition process.
8+
9+
```tsx
10+
import { Switch } from 'react-native';
11+
import { createNativeWrapper } from 'react-native-gesture-handler';
12+
13+
const RNGHSwitch = createNativeWrapper(Switch);
14+
```
15+
16+
Full example can be seen in the [Example section](#example) below.
17+
18+
This function can be useful when you want some third-party components to participate in gesture recognition process.
19+
20+
## createNativeWrapper
21+
22+
```ts
23+
function createNativeWrapper<P>(
24+
Component: React.ComponentType<P>,
25+
config: Readonly<NativeWrapperProperties> = {},
26+
detectorType: GestureDetectorType = GestureDetectorType.Native,
27+
): React.FC<P & NativeWrapperProperties>
28+
```
29+
30+
[`config`](#config) and [`detectorType`](#detectortype) parameters are optional. Their default values are described in their respective sections below.
31+
32+
This function returns original component wrapped with a specified [`GestureDetector`](/docs/fundamentals/gesture-detectors) that has a [`Native gesture`](/docs/gestures/use-native-gesture) attached to it:
33+
34+
```tsx
35+
<DetectorComponent gesture={native}>
36+
<Component {...childProps} />
37+
</DetectorComponent>
38+
```
39+
40+
### Component
41+
42+
Component to be wrapped with `Native gesture`. It can be any React component, including those from third-party libraries.
43+
44+
### config
45+
46+
Configuration for the `Native gesture` that will be attached to the wrapped component. For more details on available options, see the `Native gesture` [configuration](/docs/gestures/use-native-gesture#config) documentation. Defaults to an empty object.
47+
48+
### detectorType
49+
50+
```ts
51+
enum GestureDetectorType {
52+
Native,
53+
Virtual,
54+
Intercepting,
55+
}
56+
```
57+
58+
Type of the gesture detector that will be used to recognize the `Native gesture`. For more details on available options, see the [Gesture Detectors](/docs/fundamentals/gesture-detectors) documentation. Defaults to `GestureDetectorType.Native` (which is just [`GestureDetector`](/docs/fundamentals/gesture-detectors#gesture-detector)).
59+
60+
## onGestureUpdate_CAN_CAUSE_INFINITE_RERENDER
61+
62+
:::danger
63+
This callback may lead to infinite re-renders if not used carefully.
64+
:::
65+
66+
```ts
67+
onGestureUpdate_CAN_CAUSE_INFINITE_RERENDER?: (gesture: NativeGesture) => void;
68+
```
69+
70+
Components wrapped with `createNativeWrapper` receive an additional prop named `onGestureUpdate_CAN_CAUSE_INFINITE_RERENDER`. This callback is triggered on every update of the `Native gesture` associated with the wrapped component, providing access to the underlying gesture. This can be helpful when setting up [relations](/docs/fundamentals/gesture-composition) with other gestures.
71+
72+
73+
To prevent infinite re-renders, ensure you are not updating the component with the same gesture repeatedly, e.g.:
74+
75+
```tsx
76+
const WrappedComponent = createNativeWrapper(Component);
77+
78+
const ParentComponent = () => {
79+
const [nativeGesture, setNativeGesture] = useState<NativeGesture | null>(
80+
null
81+
);
82+
83+
const onGestureUpdate = (gesture: NativeGesture) => {
84+
if (!nativeGesture || nativeGesture.handlerTag !== gesture.handlerTag) {
85+
setNativeGesture(gesture);
86+
...
87+
}
88+
};
89+
};
90+
91+
<WrappedComponent
92+
...
93+
onGestureUpdate_CAN_CAUSE_INFINITE_RERENDER={onGestureUpdate}
94+
/>;
95+
```
96+
97+
You can also check example usage in our [`ScrollView` component](https://github.com/software-mansion/react-native-gesture-handler/blob/18af65aa7d1d425cecbdba3224271246ea739132/packages/react-native-gesture-handler/src/v3/components/GestureComponents.tsx#L80).
98+
99+
## Components wrapped using createNativeWrapper
100+
101+
Gesture Handler reexports some of the React Native components that are already wrapped using `createNativeWrapper`. These include:
102+
103+
- `Switch`
104+
- `TextInput`
105+
- `ScrollView`
106+
- `FlatList`
107+
- `RefreshControl`
108+
109+
[Buttons](/docs/components/buttons) are also wrapped using `createNativeWrapper` by default.
110+
111+
## Example
112+
113+
This example only demonstrates the usage of `createNativeWrapper`. `Switch` component from Gesture Handler comes wrapped with `createNativeWrapper` by default.
114+
115+
```tsx
116+
import { useState } from 'react';
117+
import { Switch } from 'react-native';
118+
import {
119+
GestureDetector,
120+
GestureHandlerRootView,
121+
useTapGesture,
122+
createNativeWrapper,
123+
} from 'react-native-gesture-handler';
124+
125+
const RNGHSwitch = createNativeWrapper(Switch);
126+
127+
export default function App() {
128+
const [isEnabled, setIsEnabled] = useState(false);
129+
130+
const tap1 = useTapGesture({
131+
onDeactivate: () => {
132+
console.log('Tapped!');
133+
},
134+
});
135+
136+
const tap2 = useTapGesture({
137+
onDeactivate: () => {
138+
console.log('Tapped!');
139+
},
140+
});
141+
142+
return (
143+
<GestureHandlerRootView style={{ flex: 1, paddingTop: 100 }}>
144+
<GestureDetector gesture={tap1}>
145+
<Switch value={isEnabled} onValueChange={setIsEnabled} />
146+
</GestureDetector>
147+
<GestureDetector gesture={tap2}>
148+
<RNGHSwitch value={isEnabled} onValueChange={setIsEnabled} />
149+
</GestureDetector>
150+
</GestureHandlerRootView>
151+
);
152+
}
153+
```
154+
In this scenario, the `Switch` from React Native cannot be toggled on because the `tap1` gesture intercepts it. However, when wrapped with `createNativeWrapper`, the `RNGHSwitch` becomes capable of participating in the gesture recognition process. This setup allows the switch to be toggled on while still enabling `tap2` to recognize taps on it.

packages/docs-gesture-handler/docs/guides/upgrading-to-3.mdx

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,10 @@ id: upgrading-to-3
33
title: Upgrading to the new API introduced in Gesture Handler 3
44
---
55

6-
## Migrating gestures
7-
8-
96
import CodeComparison from '@site/src/components/CodeComparison';
107

8+
## Migrating gestures
9+
1110
The most important change brought by the Gesture Handler 3 is the new hook API. Migration is pretty straightforward. Instead of calling builder methods, everything is passed as a configuration object.
1211

1312
<CodeComparison
@@ -280,6 +279,14 @@ Other components have also been internally rewritten using the new hook API but
280279

281280
</details>
282281

282+
### createNativeWrapper
283+
284+
`createNativeWrapper` has been rewritten using the new hook API and exported under the original name. The old implementation is still available as `legacy_createNativeWrapper`. It also accepts new optional parameter - `detectorType`, which allows you to specify the type of the [gesture detector](/docs/fundamentals/gesture-detectors) that will be used internally. By default it uses `GestureDetector`.
285+
286+
While new `createNativeWrapper` should work out of the box, keep in mind that it wraps your component with `GestureDetector`, which in Gesture Handler 3 is a host component. This affects view hierarchy, so depending on your use case, you might want to use [`VirtualGestureDetector`](/docs/fundamentals/gesture-detectors#virtualgesturedetector) instead. To do that, simply pass the desired detector type as the second parameter of `createNativeWrapper`.
287+
288+
More on `createNativeWrapper` can be in the [dedicated section](/docs/components/create-native-wrapper) of the documentation.
289+
283290
## Replaced types
284291

285292
Most of the types, like `TapGesture`, are still present in Gesture Handler 3. However, they are now used in new hook API. Types for old API now have `Legacy` prefix, e.g. `TapGesture` becomes `LegacyTapGesture`.

packages/react-native-gesture-handler/src/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ export { PanGestureHandler } from './handlers/PanGestureHandler';
5252
export { PinchGestureHandler } from './handlers/PinchGestureHandler';
5353
export { RotationGestureHandler } from './handlers/RotationGestureHandler';
5454
export { FlingGestureHandler } from './handlers/FlingGestureHandler';
55-
export { default as createNativeWrapper } from './handlers/createNativeWrapper';
55+
export { default as legacy_createNativeWrapper } from './handlers/createNativeWrapper';
5656
export type { NativeViewGestureHandlerProps } from './handlers/NativeViewGestureHandler';
5757
export { GestureDetector as LegacyGestureDetector } from './handlers/gestures/GestureDetector';
5858
export { GestureObjects as Gesture } from './handlers/gestures/gestureObjects';

packages/react-native-gesture-handler/src/v3/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,3 +74,5 @@ export {
7474
export type { ComposedGesture } from './types';
7575

7676
export { GestureStateManager } from './gestureStateManager';
77+
78+
export { default as createNativeWrapper } from './createNativeWrapper';

skills/gesture-handler-3-migration/SKILL.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,13 +35,15 @@ The exception to thait is `Gesture.ForceTouch` which DOES NOT have a counterpart
3535
#### Callback changes
3636

3737
In Gesture Handler 3 some of the callbacks were renamed, namely:
38+
3839
- `onStart` -> `onActivate`
3940
- `onEnd` -> `onDeactivate`
4041
- `onTouchesCancelled` -> `onTouchesCancel`
4142

4243
In the hooks API `onChange` is no longer available. Instead the `*change*` properties were moved to the event available inside `onUpdate`.
4344

4445
All callbacks of a gesture are now using the same type:
46+
4547
- `usePanGesture()` -> `PanGestureEvent`
4648
- `useTapGesture()` -> `TapGestureEvent`
4749
- `useLongPressGesture()` -> `LongPressGestureEvent`
@@ -53,6 +55,7 @@ All callbacks of a gesture are now using the same type:
5355
- `useManualGesture()` -> `ManualGestureEvent`
5456

5557
The exception to this is touch events:
58+
5659
- `onTouchesDown`
5760
- `onTouchesUp`
5861
- `onTouchesMove`
@@ -65,12 +68,14 @@ Where each callback receives `GestureTouchEvent` regardless of the hook used.
6568
In Gesture Handler 3, `stateManager` is no longer passed to `TouchEvent` callbacks. Instead, you should use the global `GestureStateManager`.
6669

6770
`GestureStateManager` provides methods for imperative state management:
71+
6872
- .begin(handlerTag: number)
6973
- .activate(handlerTag: number)
7074
- .deactivate(handlerTag: number) (.end() in the old API)
7175
- .fail(handlerTag: number)
7276

7377
`handlerTag` can be obtained in two ways:
78+
7479
1. From the gesture object returned by the hook (`gesture.handlerTag`)
7580
2. From the event inside callback (`event.handlerTag`)
7681

@@ -83,13 +88,15 @@ Callback definitions CANNOT reference the gesture that's being defined. In this
8388
`Gesture.Simultaneous(gesture1, gesture2);` becomes `useSimultaneousGestures(pan1, pan2);`
8489

8590
All relations from the old API and their counterparts in the new one:
91+
8692
- `Gesture.Race()` -> `useCompetingGestures()`
8793
- `Gesture.Simultaneous()` -> `useSimultaneousGestures()`
8894
- `Gesture.Exclusive()` -> `useExclusiveGestures()`
8995

9096
#### Cross components relations properties
9197

9298
Properties used to define cross-components interactions were renamed:
99+
93100
- `.simultaneousWithExternalGesture` -> `simultaneousWith:`
94101
- `.requireExternalGestureToFail` -> `requireToFail:`
95102
- `.blocksExternalGesture` -> `block:`
@@ -162,6 +169,12 @@ The implementation of buttons has been updated, resolving most button-related is
162169

163170
Other components have also been internally rewritten using the new hook API but are exported under their original names, so no changes are necessary on your part. However, if you need to use the previous implementation for any reason, the legacy components are also available and are prefixed with `Legacy`, e.g., `ScrollView` is now available as `LegacyScrollView`.
164171

172+
`createNativeWrapper` has been rewritten using the new hook API and exported under the original name. The old implementation is still available as `legacy_createNativeWrapper`. It also accepts new optional parameter - `detectorType`, which allows you to specify the type of the gesture detector that will be used internally. By default it uses `GestureDetector`.
173+
174+
While new `createNativeWrapper` should work out of the box, keep in mind that it wraps your component with `GestureDetector`, which in Gesture Handler 3 is a host component. This affects view hierarchy, so depending on use case, you might want to use `VirtualGestureDetector` instead.
175+
176+
Before changing, ask user about their intention - if they prefer to keep legacy version, change it to `legacy_createNativeWrapper`. If not, keep `createNativeWrapper`, then notify user that in case of problems with view hierarchy they should wrap the relevant subtree with `InterceptingGestureDetector` and pass `GestureDetectorType.Virtual` as the `detectorType` argument in `createNativeWrapper`.
177+
165178
### Replaced types
166179

167180
Most of the types used in the builder API, like `TapGesture`, are still present in Gesture Handler 3. However, they are now used in new hook API. Types for builder API now have `Legacy` prefix, e.g. `TapGesture` becomes `LegacyTapGesture`.

0 commit comments

Comments
 (0)