iOS新架构下React Native手势Modal卡顿崩溃问题求助
iOS新架构下react-native-gesture-handler下拉关闭Modal导致崩溃问题
问题说明
在iOS平台开启新架构(RCT_NEW_ARCH_ENABLED=1)时,使用react-native-gesture-handler实现的支持下拉关闭的原生Modal,在关闭Modal并切换页面后,应用会出现卡顿、崩溃或需要重启的情况,Xcode显示应用已停止运行,但控制台无报错;关闭新架构后问题消失。
环境依赖
"react-native": "0.74.3", "react-native-gesture-handler": "^2.18.1", "react-native-reanimated": "^3.14.0", "react-native-safe-area-context": "^4.10.7", "react-native-screens": "^3.32.0",
相关代码
Root App.tsx
import 'react-native-gesture-handler'; import { GestureHandlerRootView } from 'react-native-gesture-handler'; import { SafeAreaProvider } from 'react-native-safe-area-context'; import RootNavigator from './navigation/RootNavigator/RootNavigator'; const App: FC = () => ( <ThemeProvider> <LocaleProvider> <GestureHandlerRootView style={{ flex: 1 }}> <SafeAreaProvider> <RootNavigator /> </SafeAreaProvider> </GestureHandlerRootView> </LocaleProvider> </ThemeProvider> ); export default App;
CustomModal.tsx
import { PropsWithChildren, useRef } from 'react'; import { Modal, Dimensions, ModalProps, View, StyleProp, ViewStyle, Platform, SafeAreaView, Text } from 'react-native'; import { ScrollView, GestureHandlerRootView, GestureDetector, Gesture, GestureType, } from 'react-native-gesture-handler'; import { runOnJS, useSharedValue, withSpring } from 'react-native-reanimated'; import { useSafeAreaInsets } from 'react-native-safe-area-context'; import { useTheme } from '../../theme'; interface IModal extends ModalProps { onClose: () => void; headerTitle?: string; scrollEnabled?: boolean; } const { height: windowHeight } = Dimensions.get('window'); export function CustomModal({ children, onClose, headerTitle, scrollEnabled, ...props }: PropsWithChildren<IModal>) { const { layout, gutters, colors, borders, fonts, navigationTheme } = useTheme(); const translateY = useSharedValue(0); const insets = useSafeAreaInsets(); const simultaneousRef = useRef<GestureType | undefined>(undefined); const scrollViewRef = useRef<ScrollView | null>(null); const panGesture = Gesture.Pan() .onUpdate((event) => { translateY.value = event.translationY; }) .onEnd((event) => { if (event.translationY > windowHeight / 4) { runOnJS(onClose)(); } else { translateY.value = withSpring(0); } }) .simultaneousWithExternalGesture(scrollViewRef) .withRef(simultaneousRef); const contentContainerStyle: ViewStyle[] = [ layout.flexGrow_1, gutters.paddingHorizontal_16, ]; return ( <Modal animationType="slide" onRequestClose={onClose} presentationStyle="pageSheet" transparent={false} {...props} > <GestureHandlerRootView style={[layout.flex_1]}> <SafeAreaView style={[layout.flex_1]}> <GestureDetector gesture={panGesture}> <View style={[ layout.fullHeight, layout.fullWidth, gutters.paddingTop_10, ]} > <View style={[ layout.row, layout.itemsCenter, layout.justifyBetween, gutters.paddingVertical_16, ]} > <View style={[layout.flex_1]}> <Text numberOfLines={1} style={[fonts.center, fonts.size_18, fonts.lineHeight_22]} > {headerTitle} </Text> </View> <View style={[layout.flex_05, gutters.marginLeft_4]} /> </View> <ScrollView ref={scrollViewRef} contentContainerStyle={contentContainerStyle} keyboardDismissMode="on-drag" keyboardShouldPersistTaps="handled" nestedScrollEnabled scrollEnabled={scrollEnabled} simultaneousHandlers={simultaneousRef} > {children} </ScrollView> </View> </GestureDetector> </SafeAreaView> </GestureHandlerRootView> </Modal> ); }
Podfile
# Resolve react_native_pods.rb with node to allow for hoisting require Pod::Executable.execute_command('node', ['-p', 'require.resolve( "react-native/scripts/react_native_pods.rb", {paths: [process.argv[1]]}, )', __dir__]).strip platform :ios, min_ios_version_supported prepare_react_native_project! linkage = ENV['USE_FRAMEWORKS'] if linkage != nil Pod::UI.puts "Configuring Pod with #{linkage}ally linked Frameworks".green use_frameworks! :linkage => linkage.to_sym end target 'Awesome' do config = use_native_modules! use_react_native!( :path => config[:reactNativePath], # An absolute path to your application root. :app_path => "#{Pod::Config.instance.installation_root}/.." ) target 'AwesomeTests' do inherit! :complete # Pods for testing end post_install do |installer| # https://github.com/facebook/react-native/blob/main/packages/react-native/scripts/react_native_pods.rb#L197-L202 react_native_post_install( installer, config[:reactNativePath], :mac_catalyst_enabled => false, # :ccache_enabled => true ) end end
解决方案
1. 移除Modal内部的GestureHandlerRootView
根组件已经通过GestureHandlerRootView包裹了整个应用,Modal作为子组件无需再次嵌套该容器,新架构下多实例的GestureHandlerRootView会导致手势上下文冲突和内存泄漏。
修改CustomModal.tsx,删除内部的<GestureHandlerRootView>:
// 移除这一行及对应的闭合标签 // <GestureHandlerRootView style={[layout.flex_1]}> <SafeAreaView style={[layout.flex_1]}> {/* ... 其余代码保持不变 */} </SafeAreaView> // </GestureHandlerRootView>
2. 修正手势引用的管理方式
新架构下,手势引用需要使用Gesture Handler提供的Gesture.Ref()来创建,而非普通的useRef,避免类型不匹配和内存泄漏:
修改CustomModal.tsx中的引用定义:
// 替换原有的simultaneousRef定义 const simultaneousRef = Gesture.Ref(); const scrollViewRef = useRef<ScrollView>(null);
3. 组件卸载时清理手势与共享值
添加useEffect钩子,在Modal卸载时重置共享值并清理手势关联,避免内存残留:
import { useEffect } from 'react'; // 在CustomModal组件内部添加 useEffect(() => { return () => { translateY.value = 0; simultaneousRef.current?.reset(); }; }, [translateY, simultaneousRef]);
4. 确认依赖的新架构兼容性
确保所有依赖包都支持RN 0.74的新架构,可尝试升级react-native-gesture-handler到最新兼容版本(如^2.19.0),然后重新安装依赖并执行pod install:
npm install react-native-gesture-handler@latest cd ios && RCT_NEW_ARCH_ENABLED=1 pod install
5. 检查Podfile的新架构配置
确保Podfile中正确启用新架构的支持,可在use_react_native!中显式声明:
use_react_native!( :path => config[:reactNativePath], :app_path => "#{Pod::Config.instance.installation_root}/..", :new_arch_enabled => true # 添加此行 )
内容的提问来源于stack exchange,提问作者IncognitoUser
相关产品推荐
相关产品推荐

