-
Notifications
You must be signed in to change notification settings - Fork 740
Open
Labels
Description
Component: Model, ColorPicker
Platform: iOS
React Native version: 0.80.1
react-native-ui-lib: 7.44.0
react-native-gesture-handler: 2.27.2
react-native-reanimated: 3.19.0
Description
When using the BottomSheet (Build using Model component) component to display a color picker, the app sometimes freezes or crashes on iOS after selecting a color and dismissing the bottom sheet. The following error appears in the logs:
I0723 10:12:31.732117 1842884608 UIManagerBinding.cpp:135] instanceHandle is null, event of type topMomentumScrollEnd will be dropped
Related to
- Components
Steps to reproduce
Steps to reproduce the behaviour:
- Open the color picker (inside a BottomSheet).
- Select a color.
- The app may freeze or crash, and the above error appears in the logs.
Expected behavior
- The bottom sheet should close smoothly after a color is selected, regardless of scroll state.
- The app should not freeze or crash.
Actual behaviour
- On iOS, the app sometimes freezes or crashes if the bottom sheet is dismissed while a scroll/momentum event is still in progress.
- The error instanceHandle is null, event of type topMomentumScrollEnd will be dropped appears in the logs.
More Info
Code snippet
BottomSheet.tsx
import { useState, useRef, useImperativeHandle, type RefObject, type Ref, type ReactElement, type ReactNode } from 'react';
import { Dimensions, ScrollView, StyleSheet } from 'react-native';
import { GestureHandlerRootView } from 'react-native-gesture-handler';
import { View, Text, Colors, Modal } from 'react-native-ui-lib';
type BottomSheetData = string | number | object | object[] | string[] | null | undefined;
export interface BottomSheetRef {
data: RefObject<unknown>;
showBottomSheet: (popupData?: BottomSheetData) => void;
hideBottomSheet: () => void;
}
interface BottomSheetProps {
isClosable?: boolean;
isScrollEnabled?: boolean;
hasCloseButton?: boolean;
isFullScreen?: boolean;
title?: string;
subTitle?: string;
wrapperHeight?: number;
top?: number;
ref: Ref<BottomSheetRef>;
children: ReactNode;
renderHeader?: () => ReactNode;
renderFooter?: () => ReactNode;
}
function BottomSheet({ children,
ref,
isScrollEnabled = false,
isClosable = true,
isFullScreen = false,
title = '',
subTitle = '',
wrapperHeight = undefined,
top = undefined,
renderHeader = undefined,
renderFooter = undefined }: BottomSheetProps): ReactElement {
// Variables
const { height } = Dimensions.get('window');
// States initialization.
const [isBottomSheetOpen, setBottomSheetOpen] = useState(false);
// Stateless variable(ref).
const data = useRef<BottomSheetData>(null);
/** Imperative handler to declare the accessible function inside components using refs. */
useImperativeHandle(ref, () => ({
showBottomSheet,
hideBottomSheet,
data
}));
function showBottomSheet(popupData?: BottomSheetData): void {
data.current = popupData;
setBottomSheetOpen(true);
}
function hideBottomSheet(): void {
if (isClosable) {
data.current = null;
setBottomSheetOpen(false);
}
}
function renderTitle(): ReactElement | null {
if (title) {
return (
<View padding-20>
<View row>
<Text h3>{title}</Text>
</View>
{subTitle ? <Text p marginT-5>{subTitle}</Text> : null}
</View>
);
}
return null;
}
function renderScrollView(): ReactElement {
if (!isScrollEnabled) {
return (
<View height={wrapperHeight}>
{children}
</View>
);
}
return (
<ScrollView
showsVerticalScrollIndicator={false}
keyboardDismissMode="interactive"
keyboardShouldPersistTaps="always"
>
{children}
</ScrollView>
);
}
function renderBottomSheetBody(): ReactElement {
const regMaxheight = height - (top || 150);
return (
<View bottom flex>
<View centerH paddingV-15>
<View bg-opacityLight br100 width={80} height={7} />
</View>
<View
bg-white
useSafeArea
style={[{
maxHeight: isFullScreen ? '100%' : regMaxheight,
borderTopLeftRadius: 30,
borderTopRightRadius: 30
}]}
>
{renderHeader?.() ?? renderTitle()}
{renderScrollView()}
{renderFooter?.()}
</View>
</View>
);
}
return (
<GestureHandlerRootView style={styles.wrapper}>
<Modal
useKeyboardAvoidingView
navigationBarTranslucent
statusBarTranslucent
transparent
visible={isBottomSheetOpen}
animationType="slide"
overlayBackgroundColor={Colors.backdrop}
onRequestClose={hideBottomSheet}
onBackgroundPress={hideBottomSheet}
onDismiss={hideBottomSheet}
>
{renderBottomSheetBody()}
</Modal>
</GestureHandlerRootView>
);
}
const styles = StyleSheet.create({
wrapper: {
flex: 0,
zIndex: 9999
},
});
export default BottomSheet;
Toolbar.tsx
import React, { useRef } from 'react'
import { Button, ColorPicker, Colors, Text, View } from 'react-native-ui-lib';
import BottomSheet, { BottomSheetRef } from './BottomSheet';
function Toolbar({ onChange }: { onChange: (value: string) => void }) {
const bottomSheetRef = useRef<BottomSheetRef>(null);
const handleChange = (value: string) => {
onChange(value);
bottomSheetRef.current?.hideBottomSheet();
}
return (
<View>
<Text>Toolbar</Text>
<Button label="Change Color" onPress={() => bottomSheetRef.current?.showBottomSheet()} />
<BottomSheet ref={bottomSheetRef}>
<View padding-30>
<ColorPicker initialColor={Colors.blue1} colors={[]} onSubmit={handleChange} />
</View>
</BottomSheet>
</View>
)
}
export default Toolbar;
App.tsx
import { StatusBar, StyleSheet, useColorScheme } from 'react-native';
import { useState } from 'react';
import { View, Text, Colors } from 'react-native-ui-lib';
import Toolbar from './Toolbar';
function App() {
const isDarkMode = useColorScheme() === 'dark';
const [activeColor, setActiveColor] = useState(Colors.blue1);
return (
<View useSafeArea backgroundColor={Colors.white} style={styles.container}>
<StatusBar barStyle={isDarkMode ? 'light-content' : 'dark-content'} />
<Text>Active Color: {activeColor}</Text>
<View style={{backgroundColor: activeColor, width: 100, height: 100}} />
<Toolbar onChange={setActiveColor} />
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
},
});
export default App;
Workaround: delay hiding the bottom sheet to avoid crash/hang on iOS
const handleChange = (value: string) => {
onChange(value);
setTimeout(() => {
bottomSheetRef.current?.hideBottomSheet();
}, 500);
}
Environment
- React Native version: 0.80.1
- React Native UI Lib version: 7.44.0
Affected platforms
- iOS