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

React Native MapView特定返回场景卡顿原因排查求助

条码扫描跳转详情页后返回MapPage卡顿问题排查与解决

应用结构与操作流程

  • MapPage.js:包含被Modal包裹的expo-barcode-scanner组件,以及展示地图的MapView
  • DetailsPage.js:负责展示商品搜索结果或详情信息

用户可通过三种方式进入DetailsPage:

  1. 点击MapView标记的callout弹窗
  2. 在MapPage的TextInput中输入商品ID提交搜索
  3. 打开条码扫描Modal扫描商品条码

跳转规则:搜索有结果则停留在DetailsPage;无结果则自动调用navigate.goBack()返回MapPage

卡顿复现场景

仅在以下操作路径出现卡顿:

条码扫描有结果 → 停留在DetailsPage → 手动返回MapPage

其余场景均无卡顿:

  • 点击标记弹窗跳转后返回
  • TextInput搜索后返回(无论有无结果)
  • 条码扫描无结果自动返回

参考代码

MapPage核心代码

<MapView
    style={{ width: '100%', height: '100%' }}
    provider={PROVIDER_GOOGLE}
    showsMyLocationButton={true}
    showsUserLocation={true}
    region={{
        latitude: region.latitude,
        longitude: region.longitude,
        latitudeDelta: region.latitudeDelta,
        longitudeDelta: region.longitudeDelta,
    }}>
    <MapMarker
        markerLocation={markers}
        countryCode={country.countryCode}
    />
</MapView>
...
<TextInput
    placeholder={i18n.t('SearchId')}
    placeholderTextColor="white"
    onSubmitEditing={() =>
        handleBarCodeScanned({ data: textSearchData })
    }
    onChangeText={(text) => {
        settextSearchData(text.toUpperCase());
    }}
/>
<Pressable onPress={() => setqrcodeModalVisi(true)}>
    <Ionicons
        name="qr-code-outline"
        size={33}
        color="white"
    />
</Pressable>
...
<Modal
    visible={qrcodeModalVisi}
    animationType="slide"
    onRequestClose={() => setqrcodeModalVisi(false)}
    transparent={true}>
    <View>
        <Pressable onPress={() => setqrcodeModalVisi(false)}>
            <Feather
                name="x"
                size={24}
                color="white"
            />
        </Pressable>
        {!hasPermission && (
            <View>
                <Text style={{ color: colors.text }}>
                    Camera access denied.
                </Text>
                <Pressable onPress={() => getBarCodeScannerPermissions()}>
                    <Text style={{ color: colors.secondary }}>
                        Goto setting page
                    </Text>
                </Pressable>
            </View>
        )}
        {hasPermission && (
            <View
                style={{
                    flex: 1,
                    justifyContent: 'center',
                    alignItems: 'center',
                }}>
                <BarCodeScanner
                    onBarCodeScanned={
                        scanned ? undefined : handleBarCodeScanned
                    }
                    style={{
                        width: '100%',
                        height: '80%',
                    }}
                />
            </View>
        )}
    </View>
</Modal>

MapMarker组件代码

<View>
    {markerLocation.map((array) => (
        <Marker
            key={array.title}
            coordinate={{
                latitude: array.latitude,
                longitude: array.longitude,
            }}
            image={require('../../../assets/icons/map_marker.png')}
            title={''}
            description={''}
            onCalloutPress={() => handleMapMarkerPress(array.regionCode)}>
            <Callout>
                <View
                    style={{
                        backgroundColor: 'white',
                        width: 180,
                        paddingVertical: 5,
                        alignItems: 'center',
                        justifyContent: 'center',
                    }}>
                    <Text
                        style={{
                            width: '80%',
                            textAlign: 'center',
                            color: colors.secondary,
                            fontWeight: 'bold',
                        }}>
                        {array.title}
                    </Text>
                    <View
                        style={{
                            backgroundColor: colors.secondary,
                            width: '80%',
                            height: 25,
                            alignItems: 'center',
                            justifyContent: 'center',
                            marginTop: 2,
                            flexDirection: 'row',
                            marginVertical: 5,
                        }}>
                        <Text style={{ color: 'white', fontWeight: 'bold' }}>
                            {i18n.t('MapMarkerBtn')}
                        </Text>
                    </View>
                </View>
            </Callout>
        </Marker>
    ))}
</View>

问题根源分析

卡顿仅出现在条码扫描有结果后手动返回的场景,核心原因是:
扫描完成后,条码Modal的qrcodeModalVisi状态未及时关闭,导致返回MapPage时,Modal仍处于挂载状态(即使不可见),同时BarCodeScanner组件未被卸载,持续占用相机资源,和MapView的地图渲染抢占系统资源,引发卡顿。

其他场景无卡顿的原因:

  • 标记弹窗/TextInput跳转:没有涉及Modal和相机资源的残留
  • 条码扫描无结果:自动返回时可能触发了Modal关闭逻辑(或未停留详情页,资源未长时间占用)

解决方案

1. 扫描完成后立即关闭Modal

修改handleBarCodeScanned函数,无论搜索结果是否存在,先关闭条码Modal,释放相机资源:

const handleBarCodeScanned = ({ data }) => {
  // 优先关闭Modal,避免资源残留
  setqrcodeModalVisi(false);
  setScanned(true);

  // 执行商品搜索逻辑
  fetchProduct(data)
    .then(product => {
      if (product) {
        navigation.navigate('DetailsPage', { product });
      } else {
        navigation.goBack();
      }
    })
    .catch(() => navigation.goBack());
};

2. 监听Modal状态,重置扫描器

通过useEffect监听Modal可见性,关闭时重置scanned状态,确保下次打开Modal时扫描器正常工作,同时避免无效渲染:

import { useEffect } from 'react';

useEffect(() => {
  if (!qrcodeModalVisi) {
    setScanned(false);
  }
}, [qrcodeModalVisi]);

3. 优化MapMarker组件重渲染

用React.memo包裹MapMarker组件,减少不必要的重渲染,避免和Modal残留资源叠加导致卡顿:

import React, { memo } from 'react';

const MapMarker = memo(({ markerLocation, countryCode }) => {
  // 原组件内容
});

export default MapMarker;

4. 组件卸载时清理资源

在MapPage卸载时,强制关闭Modal并重置扫描状态,防止资源泄漏:

useEffect(() => {
  return () => {
    setqrcodeModalVisi(false);
    setScanned(true);
  };
}, []);

验证效果

修改后,条码扫描有结果跳转详情页前,Modal已关闭,BarCodeScanner被卸载,返回MapPage时仅渲染MapView,不会出现资源抢占,卡顿问题即可解决。

内容的提问来源于stack exchange,提问作者Jess

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.09 14:20:58