App Freezes on iOS When Dismissing Model After ColorPicker Selection
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
Hello,
We have a version that supports new-arch (RN77), you can use the next tag for now.
Please make sure to go over the v8 doc, it includes breaking changes and some known issues.
Please close this ticket if it solved your bug.