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

Expo React Native应用遭遇Too many re-renders错误求解决

问题分析与修复

核心错误原因

  1. Firebase监听直接写在组件体内:onChildAdded直接放在组件函数里,每次组件渲染都会重新注册监听,回调里调用setOtherRiders触发重渲染,形成无限循环。
  2. useEffect无依赖数组:你的useEffect没有指定依赖项,导致每次组件渲染都会执行requestLocationPermission,如果这个函数里调用了setMyCurrentLocation或setIsMapReady,就会再次触发重渲染,循环往复。

修复步骤

1. 将Firebase监听移入useEffect并清理监听

把onChildAdded的注册逻辑放到useEffect里,并且返回清理函数移除监听,避免重复注册。同时注意数据格式,确保otherRiders是数组类型。

2. 给useEffect添加正确的依赖数组

requestLocationPermission的useEffect只需要在组件挂载时执行一次,所以给它加空依赖数组[];如果需要根据myCurrentLocation变化重新执行,再添加对应依赖,但这里应该只初始化一次。

3. 渲染其他骑手的标记

在MapView里遍历otherRiders渲染对应的Marker。

修改后的完整代码

import React, { useState, useEffect } from "react";
import { View, Text, ScrollView, StyleSheet, TextInput, Button, Switch, Dimensions, Image } from "react-native";
import { useNavigation } from '@react-navigation/native';
import MapView, { Marker, Polyline } from 'react-native-maps';
import { requestLocationPermission, getCurrentLocation, writeMyLocationData } from '../../sockets/googlemap'
import { findPlace, fetchRoute } from '../../sockets/googleroute'
import { ref, onChildAdded, off } from "firebase/database";
import { database } from '../../firebaseConfig';

const Main = () => {
    const [otherRiders, setOtherRiders] = useState([]);
    const [routeInfo, setRouteInfo] = useState({ origin: "", destination: "" });
    const navigation = useNavigation();
    const [myCurrentLocation, setMyCurrentLocation] = useState({
        latitude: 0,
        longitude: 0,
    })
    const [myDestination, setMyDestination] = useState({
        latitude: 0,
        longitude: 0,
    })
    const [myOrigin, setMyOrigin] = useState({
        latitude: 0,
        longitude: 0,
    })
    const [isMapReady, setIsMapReady] = useState(false);
    const [isEnabled, setIsEnabled] = useState(false);
    const toggleSwitch = () => setIsEnabled(previousState => !previousState);
    const iconImage = require('../../assets/kurumi.jpg');
    const myDestinationIcon = require('../../assets/flag.png');
    const riderIcon = require('../../assets/your-rider-icon.png'); // 替换成你的骑手图标

    // 初始化位置权限和监听Firebase数据
    useEffect(() => {
        // 请求位置权限
        requestLocationPermission(setMyCurrentLocation, myCurrentLocation, setIsMapReady);

        // Firebase数据监听
        const dbref = ref(database); // 确保这里是正确的路径,比如'/riders'
        const handleChildAdded = (snapshot) => {
            let data = snapshot.val();
            // 确保数据是数组格式,如果是单个对象就转成数组
            const newRiders = Array.isArray(data) ? data : [data];
            setOtherRiders(prev => [...prev, ...newRiders]);
            console.log("获取到骑手数据:", newRiders);
        };

        onChildAdded(dbref, handleChildAdded);

        // 清理函数:组件卸载时移除监听
        return () => {
            off(dbref, 'child_added', handleChildAdded);
        };
    }, []); // 空依赖数组,只执行一次

    return (
        isMapReady ? (
            <ScrollView style={styles.Wrapper}>
                <View style={styles.mapContainer}>
                    <MapView style={styles.map}
                        initialRegion={{
                            latitude: myCurrentLocation.latitude,
                            longitude: myCurrentLocation.longitude,
                            latitudeDelta: 0.0922,
                            longitudeDelta: 0.0421,
                        }}
                    >
                        {/* 自己的位置标记 */}
                        <Marker
                            coordinate={{
                                latitude: myCurrentLocation.latitude,
                                longitude: myCurrentLocation.longitude,
                            }}
                        >
                            <Image style={styles.icon} source={iconImage} />
                        </Marker>
                        {/* 目的地标记 */}
                        <Marker
                            coordinate={{
                                latitude: myDestination.latitude,
                                longitude: myDestination.longitude,
                            }}
                        >
                            <Image style={styles.icon} source={myDestinationIcon} />
                        </Marker>
                        {/* 其他骑手的标记 */}
                        {otherRiders.map((rider, index) => (
                            <Marker
                                key={rider.user_id || index} // 优先用user_id当key,无则用index
                                coordinate={{
                                    latitude: rider.latitude,
                                    longitude: rider.longitude,
                                }}
                            >
                                <Image style={styles.icon} source={riderIcon} />
                            </Marker>
                        ))}
                        {/* 路线 */}
                        <Polyline
                            coordinates={[
                                { latitude: myOrigin.latitude, longitude: myOrigin.longitude },
                                { latitude: myDestination.latitude, longitude: myDestination.longitude },
                            ]}
                            strokeColor="#FF0000"
                            strokeWidth={2}
                        />
                    </MapView>
                </View>
                <View>
                    <Text style={styles.direction}>出発地</Text>
                    <TextInput
                        placeholder={'出発地を入力してください'}
                        value={routeInfo.origin}
                        autoCapitalize="none"
                        onChangeText={text => setRouteInfo(prev => ({ ...prev, origin: text }))}
                        style={styles.imputBox}
                    />
                </View>
                <View>
                    <Text style={styles.direction}>目的地</Text>
                    <TextInput
                        placeholder={'到着地を入力してください'}
                        value={routeInfo.destination}
                        autoCapitalize="none"
                        onChangeText={text => setRouteInfo(prev => ({ ...prev, destination: text }))}
                        style={styles.imputBox}
                    />
                </View>
                <View>
                    <Button
                        title="ルートを検索する"
                        onPress={() => fetchRoute(setMyOrigin, setMyDestination, routeInfo)}
                    />
                </View>
                <View>
                    <Text style={styles.infomation}>位置情報共有をONにする</Text>
                    <Switch
                        trackColor={{ false: '#767577', true: '#81b0ff' }}
                        thumbColor={isEnabled ? '#f5dd4b' : '#f4f3f4'}
                        ios_backgroundColor="#3e3e3e"
                        onValueChange={toggleSwitch}
                        value={isEnabled}
                    />
                </View>
                <View>
                    <Button
                        title="戻る"
                        onPress={() => navigation.navigate('Top')}
                    />
                </View>
            </ScrollView>
        ) : (
            <ScrollView style={styles.waitScrollViewContent}>
                <View style={styles.center}>
                    <Text style={styles.infomation}>少々お待ちください。。。</Text>
                </View>
            </ScrollView>
        )
    )
}

export default Main;

const { height } = Dimensions.get("window");
const mapHeight = height * 0.5;
const styles = StyleSheet.create({
    Wrapper: {
        flex: 1,
    },
    direction: {
        fontSize: 12,
        padding: 10
    },
    infomation: {
        fontSize: 12,
        padding: 10
    },
    subTitle: {
        fontSize: 14,
        textAlign: "center",
        padding: 5
    },
    mapContainer: {
        height: mapHeight,
        justifyContent: 'center',
        alignItems: 'center',
    },
    map: {
        ...StyleSheet.absoluteFillObject,
    },
    markerContainer: {
        backgroundColor: 'blue',
        padding: 5,
        borderRadius: 5,
    },
    markerText: {
        color: 'white',
        fontWeight: 'bold',
    },
    imputWrapper: {
        alignItems: "center",
        padding: 20
    },
    imputContainer: {
        width: '100%',
        marginBottom: 10
    },
    imputBox: {
        height: 40,
        borderWidth: 1,
    },
    icon: {
        width: 28,
        height: 28,
    },
    waitScrollViewContent: {
        flex: 1,
        width: '100%',
    },
    center: {
        flex: 1,
        justifyContent: 'center',
        alignItems: 'center',
    },
});

额外注意点

  • 确保Firebase的ref路径正确,比如如果骑手数据存在/riders节点下,要写成ref(database, 'riders'),不然会监听整个数据库的所有变化。
  • 如果requestLocationPermission内部会频繁更新myCurrentLocation,可以考虑在useEffect里添加对应的依赖,但要注意避免无限循环,必要时用useCallback包裹函数。

内容的提问来源于stack exchange,提问作者谷口祐人

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.24 16:14:56