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

Posted

技术标签:

【中文标题】我的 setstate 方法被多次调用,这导致在 React Native 中删除项目时出现问题【英文标题】:My setstate method is called multiple times which is causing a problem in deleting items in React Native 【发布时间】:2022-01-01 17:13:04 【问题描述】:

我是 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>
  );


);

【问题讨论】:

【参考方案1】:

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

    onPress=onRemove(id) 应该是 onPress=() =&gt; onRemove(id)。您需要提供一个函数作为onPress 属性,而不是调用函数的结果

    每当您基于现有状态设置新状态时,最佳实践(通常是必要实践)使用函数的回调版本,以便您在最高-日期状态。你传入一个接收最新状态的函数

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

旁注:您的部分代码期望 renderData 是一个数组,但您的另一部分代码正在执行 setRenderData(),这将使其成为非数组对象。碰巧useEffect 的清理回调的一部分没有依赖项,因此组件不会使用该对象(因为仅在卸载组件时才进行清理),但这仍然不是最佳实践。 (也不需要这样做,卸载组件时会释放所有状态。)

【讨论】:

感谢您的回复。我在我的代码中进行了两项更改。在我进行第一次更改后,删除不起作用,我的意思是当我检查日志时该函数没有调用。 onRemove(id)> @spykeburn - 然后发生了 else 的事情。 onPress=() =&gt; onRemove(id) 是正确的。 我的 onRemove 函数写错了吗?因为它正在使用上一行 onPressonRemove(id)【参考方案2】:

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

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

我不认为id =&gt; e=&gt; 是一个有效的语法,它应该是这样的:

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 更改时调用,而不是在每次渲染时调用它们

【讨论】:

谢谢。现在调用了删除函数,但这条线 setRenderData(res.data) 仍然被多次调用,因此即使在删除之后,列表也会填充原始列表。 我只是在我的帖子中添加了一个新答案,请查看 好的,那么我应该如何或在哪里调用 handleUploadPhoto() 方法? 我刚刚又编辑了一遍,建议看最后一部分

以上是关于我的 setstate 方法被多次调用,这导致在 React Native 中删除项目时出现问题的主要内容,如果未能解决你的问题,请参考以下文章

在循环中调用 setState 只会更新状态 1 次

在 React 组件中多次使用 this.setState 会发生啥?

多次调用函数时 setState 不会发生变化

React的setState执行机制

UICollectionView CellForItemAt 在滚动时被多次调用,导致单元格文本重叠

React:setState 不会导致重新渲染?