You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.20 08:30:56