Skip to content

Commit d0849a1

Browse files
committed
Wrap children
1 parent f922b3b commit d0849a1

2 files changed

Lines changed: 59 additions & 47 deletions

File tree

packages/react-native-gesture-handler/src/v3/components/GestureComponents.tsx

Lines changed: 24 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,10 @@ import { GestureDetectorType } from '../detectors';
2121
import type { NativeGesture } from '../hooks/gestures/native/NativeTypes';
2222
import { NativeWrapperProps } from '../hooks/utils';
2323
import type { NativeWrapperProperties } from '../types/NativeWrapperType';
24-
import ScrollViewResponderInterceptor from './ScrollViewResponderInterceptor';
24+
import {
25+
interceptScrollViewChildren,
26+
ScrollViewResponderProvider,
27+
} from './ScrollViewResponderInterceptor';
2528

2629
export const RefreshControl: React.ComponentType<
2730
RNRefreshControlProps &
@@ -79,26 +82,26 @@ export const ScrollView = (
7982
};
8083

8184
return (
82-
<GHScrollView
83-
{...rest}
84-
ref={props.ref}
85-
keyboardShouldPersistTaps={keyboardShouldPersistTaps}
86-
onGestureUpdate_CAN_CAUSE_INFINITE_RERENDER={updateGesture}
87-
// @ts-ignore we don't pass `refreshing` prop as we only want to override the ref
88-
refreshControl={
89-
refreshControl
90-
? React.cloneElement(
91-
refreshControl,
92-
// @ts-ignore block exists (on our RefreshControl)
93-
scrollGesture ? { block: scrollGesture } : {}
94-
)
95-
: undefined
96-
}>
97-
<ScrollViewResponderInterceptor
98-
keyboardShouldPersistTaps={keyboardShouldPersistTaps}>
99-
{children}
100-
</ScrollViewResponderInterceptor>
101-
</GHScrollView>
85+
<ScrollViewResponderProvider
86+
keyboardShouldPersistTaps={keyboardShouldPersistTaps}>
87+
<GHScrollView
88+
{...rest}
89+
ref={props.ref}
90+
keyboardShouldPersistTaps={keyboardShouldPersistTaps}
91+
onGestureUpdate_CAN_CAUSE_INFINITE_RERENDER={updateGesture}
92+
// @ts-ignore we don't pass `refreshing` prop as we only want to override the ref
93+
refreshControl={
94+
refreshControl
95+
? React.cloneElement(
96+
refreshControl,
97+
// @ts-ignore block exists (on our RefreshControl)
98+
scrollGesture ? { block: scrollGesture } : {}
99+
)
100+
: undefined
101+
}>
102+
{interceptScrollViewChildren(children)}
103+
</GHScrollView>
104+
</ScrollViewResponderProvider>
102105
);
103106
};
104107

packages/react-native-gesture-handler/src/v3/components/ScrollViewResponderInterceptor.tsx

Lines changed: 35 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import type { PropsWithChildren } from 'react';
2-
import React, { useCallback, useEffect, useMemo, useRef } from 'react';
2+
import React, { use, useCallback, useEffect, useMemo, useRef } from 'react';
33
import type {
44
EmitterSubscription,
55
KeyboardEvent,
@@ -95,7 +95,7 @@ type ScrollViewResponderInterceptorProps = PropsWithChildren<{
9595
keyboardShouldPersistTaps?: RNScrollViewProps['keyboardShouldPersistTaps'];
9696
}>;
9797

98-
const ScrollViewResponderInterceptor = ({
98+
export const ScrollViewResponderProvider = ({
9999
children,
100100
keyboardShouldPersistTaps,
101101
}: ScrollViewResponderInterceptorProps) => {
@@ -110,44 +110,53 @@ const ScrollViewResponderInterceptor = ({
110110
return () => unsubscribeFromKeyboardVisibility();
111111
}, []);
112112

113+
return (
114+
<JSResponderContext value={contextValue}>{children}</JSResponderContext>
115+
);
116+
};
117+
118+
const LogicalResponderChild = ({ children }: PropsWithChildren) => {
119+
const jsResponderContext = use(JSResponderContext);
120+
113121
const resetRNGHResponderEvent = useCallback(() => {
114-
isRNGHResponderEvent.current = false;
122+
updateResponderEventValue(jsResponderContext, false);
115123
return false;
116-
}, []);
124+
}, [jsResponderContext]);
117125

118126
const handleStartShouldSetResponder = useCallback(() => {
119127
const shouldHandleRNGHEvent =
120-
keyboardShouldPersistTaps === 'handled' && isRNGHResponderEvent.current;
128+
jsResponderContext?.keyboardShouldPersistTaps === 'handled' &&
129+
jsResponderContext.isRNGHResponderEvent.current;
121130

122-
isRNGHResponderEvent.current = false;
131+
updateResponderEventValue(jsResponderContext, false);
123132

124133
return shouldHandleRNGHEvent;
125-
}, [keyboardShouldPersistTaps]);
126-
127-
// RNGH tap responders need to let RN components higher in the tree handle
128-
// the JS responder event first. If no RN component claims it, this logical
129-
// ScrollView child consumes the marked event before ScrollView's own
130-
// keyboardShouldPersistTaps='handled' responder logic handles it.
131-
// For more information check this comment:
132-
// https://github.com/software-mansion/react-native-gesture-handler/pull/4158#issuecomment-4431632964
134+
}, [jsResponderContext]);
135+
133136
return (
134-
<JSResponderContext value={contextValue}>
135-
<View
136-
collapsable={false}
137-
onStartShouldSetResponderCapture={resetRNGHResponderEvent}
138-
onStartShouldSetResponder={handleStartShouldSetResponder}
139-
pointerEvents="box-none"
140-
style={styles.logicalResponder}>
141-
{children}
142-
</View>
143-
</JSResponderContext>
137+
<View
138+
collapsable={false}
139+
onStartShouldSetResponderCapture={resetRNGHResponderEvent}
140+
onStartShouldSetResponder={handleStartShouldSetResponder}
141+
pointerEvents="box-none"
142+
style={styles.logicalResponder}>
143+
{children}
144+
</View>
144145
);
145146
};
146147

148+
export function interceptScrollViewChildren(children: React.ReactNode) {
149+
return React.Children.map(children, (child) =>
150+
child == null || typeof child === 'boolean' ? (
151+
child
152+
) : (
153+
<LogicalResponderChild>{child}</LogicalResponderChild>
154+
)
155+
);
156+
}
157+
147158
const styles = StyleSheet.create({
148159
logicalResponder: {
149160
display: 'contents',
150161
},
151162
});
152-
153-
export default ScrollViewResponderInterceptor;

0 commit comments

Comments
 (0)