我的 setstate 方法被多次调用,这导致在 React Native 中删除项目时出现问题

My setstate method is called multiple times which is causing a problem in deleting items in React Native

我是 React Native 的新手,正在做一个小项目。我正在调用网络调用并在 FlatList 中呈现项目。我在删除项目时遇到问题,因为连续调用了 setRenderData 方法。我如何防止这种情况发生?我怎么只调用它一次?我尝试了一些解决方案,但我不明白问题出在哪里。请帮助我。

export default function TextExtractor() {
  const [pickerResponse, setPickerResponse] = useState(null);
  const [visible, setVisible] = useState(false);
  var [renderData, setRenderData] = useState([]);
const onRemove = id => e => {
   setRenderData(renderData.filter(renderData => renderData.Id !== id))
   };
   const Item = ({ title,id }) => (
     <View style={styles.item}>
       <Text style={styles.title}>{title}</Text>
    <TouchableOpacity  onPress={onRemove(id)}>
       <Image
style={{width:30,height:30}}
            source={require('../assets/delete.png')}
          />
          </TouchableOpacity>
     </View>
   );

  useEffect(() => {
   
    return () => {
      setRenderData({});
    };
}, []);

  const onImageLibraryPress = useCallback(() => {
  
    const options = {
      selectionLimit: 1,
      mediaType: 'photo',
      includeBase64: false,
    };
    ImagePicker.launchImageLibrary(options, setPickerResponse);
  }, []);

  const onCameraPress = useCallback(() => {
    const options = {
      saveToPhotos: true,
      mediaType: 'photo',
      includeBase64: false,
      maxWidth: 500,
      maxHeight: 500,
      quality: 0.5,
    };
    ImagePicker.launchCamera(options, setPickerResponse);
  }, []);

  const handleUploadPhoto = () => {
    setVisible(false);
  
     const data = new FormData();
   

      data.append("file_uploaded", {
         name: pickerResponse.assets[0].fileName,
         type: pickerResponse.assets[0].type,
         uri:
           Platform.OS === "android"
             ? pickerResponse.assets[0].uri
             : pickerResponse.assets[0].uri.replace("file://", "")
       }); 
 
       var url ='https://bacodetextextract.com/upload/image/';
   
       axios.post(url, data, {headers: {
         "Content-Type": "multipart/form-data",
         Accept: "application/json"
        
       }})
       .then((res) => {
    
        setRenderData(res.data);
     

         console.log(‘RESULT: ',renderData);

 
       })
       .catch((err) => {
         console.log('error', err);
       })
   };

  const uri = pickerResponse?.assets && pickerResponse.assets[0].uri;

  if(uri!==undefined){
 

  handleUploadPhoto();
  }

  const renderItem = ({ item }) => (
    <Item title={item.Text}
    id = {item.Id}
    />
  );
  return (
    <View style={styles.screen}>
 
      <ImagePickerAvatar uri={uri} onPress={() => setVisible(true)} />

      <FlatList style={styles.usernameText}
        data={renderData}
        renderItem={renderItem}
        keyExtractor={item => item.Id}
      />
     
      <ImagePickerModal
        isVisible={visible}
        onClose={() => setVisible(false)}
        onImageLibraryPress={onImageLibraryPress}
        onCameraPress={onCameraPress}
      />


    </View>
  );
}

});

重复调用setRenderData不是问题,但是你的代码有两个问题:

  1. onPress={onRemove(id)} 应该是 onPress={() => onRemove(id)}。您需要提供函数作为 onPress 属性,而不是调用函数的 结果

  2. 每当您根据现有状态设置新状态时,最好的做法(通常是 必要的 做法)使用函数的回调版本,因此您正在以最新状态运行。您传入接收最新状态的函数

    setRenderData(renderData => renderData.filter(
        renderData => renderData.Id !== id
    ));
    

旁注:您的部分代码期望 renderData 是一个数组,但您的另一部分代码正在做 setRenderData({}),这将使它成为一个非数组对象。碰巧 useEffect 的清理回调的一部分没有依赖关系,因此组件不会使用该对象(因为该清理仅在组件卸载时完成),但它仍然不是最好的实践。 (也没有必要这样做,当组件被卸载时所有状态都会被释放。)

T.J.Crowder 通过重复重新渲染解决了您的问题。第二个问题是你的这部分代码:

const onRemove = id => e => {
   setRenderData(renderData.filter(renderData => renderData.Id !== id))
   };

我不认为 id => e=> {} 是一个有效的语法,它应该是这样的:

const onRemove = (id) => {
   setRenderData(renderData.filter(renderData => renderData.Id !== id))
   };

我找到了你一直打电话给 setRenderData 的另一个原因。您在此函数中有 setRenderData :

onst handleUploadPhoto = () => {
  setVisible(false);

  const data = new FormData();
  

  data.append("file_uploaded", {
    name: pickerResponse.assets[0].fileName,
    type: pickerResponse.assets[0].type,
    uri:
      Platform.OS === "android"
        ? pickerResponse.assets[0].uri
        : pickerResponse.assets[0].uri.replace("file://", "")
  }); 

  var url ='https://bacodetextextract.com/upload/image/';

  axios.post(url, data, {headers: {
    "Content-Type": "multipart/form-data",
    Accept: "application/json"
  
  }})
  .then((res) => {

  setRenderData(res.data);


    console.log(‘RESULT: ',renderData);


  })
  .catch((err) => {
    console.log('error', err);
  })
};

并且每次重新渲染它时都会再次调用此行。如果 uri 不是 undefined 那么它将一次又一次地调用 handleUploadPhoto

const uri = pickerResponse?.assets && pickerResponse.assets[0].uri;

if(uri!==undefined){
  handleUploadPhoto();
}

我的建议是这样:

useEffect(()=>{
  const uri = pickerResponse?.assets && pickerResponse.assets[0].uri;

  if(uri!==undefined){
    handleUploadPhoto();
  }
},[pickerResponse])

在上面的代码中,我们将导致问题的代码放在 useEffect 中,这样它只会在 pickerResponse 更改时调用,而不是在每次渲染时调用它们