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

React组件切换时报无法对已卸载组件更新状态警告如何解决

问题修复方案

核心问题原因

  • 你在Photo组件中优先调用props.onChange(false)触发父组件切换回Game,直接卸载了当前Photo组件,后续异步请求的回调、状态更新操作就会触发「卸载组件更新状态」警告
  • Game组件中的位置监听watchPositionAsync未配置卸载清理函数,组件卸载后仍会触发位置更新的状态修改
  • 直接对state中的targets数组调用splice修改原数组,属于状态突变,会引发不可预期的渲染异常
  • Photo组件的return中直接写Alert.alert,每次渲染都会重复触发弹窗,逻辑错误

修复代码

1. Photo.js 全量修改

import React, { useState, useEffect, useRef } from 'react';
import { StyleSheet, Text, View, Alert, Button, Image } from 'react-native';
import * as ImagePicker from 'expo-image-picker';
import * as Permissions from 'expo-permissions';
import * as Location from 'expo-location';
export default function PhotoHandler(props) {
    const [pickedLocation, setPickedLocation] = useState({
        latitude: null,
        longitude: null
    });
    const [pickedImage, setPickedImage] = useState(null); 
    // 挂载状态标记,避免卸载后执行状态更新
    const isMounted = useRef(true);

    useEffect(() => {
        // 组件挂载时触发弹窗,不要写在return中避免重复触发
        Alert.alert(
        "Success",
        "Take photo",
        [
          {text: "Open Camera",
          onPress: imagetakenHandler
          }
        ]
      )
      // 卸载时更新挂载标记
      return () => {
        isMounted.current = false;
      }
    }, [])

    const verifyPermissions = async () =>{
        const result = await Permissions.askAsync(Permissions.CAMERA_ROLL, Permissions.CAMERA);
        if(result.status != 'granted'){
            Alert.alert(
                'Insufficient Permissions!',
                'You need to grant camera permissions to use this app',
                [{text: 'Okay'}]
            );
            return false;
        }
            return true;

    };
    const imagetakenHandler = async () =>{
        if(!isMounted.current) return
        const hasPermission = await verifyPermissions();
        if(!hasPermission) return;
        const image = await ImagePicker.launchCameraAsync({
              mediaTypes: ImagePicker.MediaTypeOptions.Images,
              quality: 1,
              aspect: [1, 1],
              base64: true,
              exif: true
          });
        if(!image.cancelled && isMounted.current){
            setPickedImage(image.base64);
        }
    };
    useEffect(() => {
        const requestOptions = {
            method: 'POST',
            headers: {'Content-type': 'application/x-www-form-urlencoded'},
        };
        if(pickedImage != null && isMounted.current){
            // 先完成上传请求,所有操作结束后再切换组件
            fetch(`https://xxx.yy.zz`, requestOptions)
            .then(response => response.json())
            .then(res => {
                console.log('SUCCESS: '+res)
                if(isMounted.current) props.onChange(false);
            })
            .catch(e => {
                console.log(e);
                // 报错也可切换回Game,可根据业务需求调整
                if(isMounted.current) props.onChange(false);
            });
        }
}, [pickedImage])
    return (
    <View style={styles.container}>
    </View>
    );
}
const styles = StyleSheet.create({
    container: {
        flex: 1,
        marginTop: 30

    }
});

2. Game.js 关键逻辑修改

// 位置监听的useEffect新增清理函数,卸载时停止位置监听
useEffect(() => {
    console.log("Inside UseEffect watch position"); 
    let locationSubscription = null;
    (async () => {
        try {
            locationSubscription = await Location.watchPositionAsync(
                {
                    accuracy: Location.Accuracy.BestForNavigation,
                    distanceInterval: 10,
                    timeInterval: 3000,
                },
                (loc) => {
                    setPickedLocation({
                        latitude: loc.coords.latitude,
                        longitude: loc.coords.longitude
                    });
                }
            );
        } catch (e) {
            Alert.alert("Error");
        }
    })();
    // 组件卸载时清理监听
    return () => {
        if(locationSubscription) locationSubscription.remove();
    }
}, []);

// 计算距离的useEffect修正targets状态修改逻辑,避免直接修改原state
useEffect(() => {
    console.log('inside useEffect hook for measuring targets distances');
    var lat1 = pickedLocation.latitude; 
    var lng1 = pickedLocation.longitude;
    console.log("targets distance");
    const R = 6371;
    let targets_distance = [];
    // 浅拷贝targets,不直接修改原状态数组
    let newTargets = [...targets];
    let shouldSwitchToPhoto = false;
    for (i = 0; i < newTargets.length; i++) {
        const φ1 = lat1 * Math.PI / 180;
        const φ2 = newTargets[i].lat * Math.PI / 180;
        const Δφ = (newTargets[i].lat - lat1) * Math.PI / 180;
        const Δλ = (newTargets[i].lng - lng1) * Math.PI / 180;
        const a = Math.sin(Δφ / 2) * Math.sin(Δφ / 2) +
            Math.cos(φ1) * Math.cos(φ2) *
            Math.sin(Δλ / 2) * Math.sin(Δλ / 2);
        const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
        const distance = R * c * 1000; 
        console.log('distance is'+ distance);
        if(distance <=20){
            shouldSwitchToPhoto = true; 
            newTargets.splice(i, 1);
            i--; // 数组长度变化后修正索引避免漏遍历
            console.log("targets after removal");
            console.log(newTargets);
        }
        else{
            targets_distance.push(distance);
        }
    }
    // 同步更新targets状态
    setTargets(newTargets);
    if(shouldSwitchToPhoto){
        setConditionCounter(true); //切换到photo组件
    }
    console.log(targets_distance);
    var index = 0;
    var value = targets_distance[0];
    for (var i = 1; i < targets_distance.length; i++) {
        if (targets_distance[i] < value) {
            value = targets_distance[i];
            index = i;
        }
    }
    console.log("smallest element is " + value + " at index " + index);
    setIndex(index);
    setDistances(targets_distance);
}, [pickedLocation]);

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.04 23:57:04