Мой метод setstate вызывается несколько раз, что вызывает проблему при удалении элементов в React Native

#javascript #reactjs #react-native #react-hooks #react-native-flatlist

Вопрос:

Я новичок в React Native и работаю над небольшим проектом. Я вызываю сетевой вызов и отображаю элементы в плоском списке. У меня возникают проблемы при удалении элементов, потому что метод setRenderData вызывается непрерывно. Как мне предотвратить это? как я могу назвать это только один раз? Я пробовал некоторые решения, но не могу понять, в чем проблема. Пожалуйста, помогите мне.

 export default function TextExtractor() {  const [pickerResponse, setPickerResponse] = useState(null);  const [visible, setVisible] = useState(false);  var [renderData, setRenderData] = useState([]); const onRemove = id =gt; e =gt; {  setRenderData(renderData.filter(renderData =gt; renderData.Id !== id))  };  const Item = ({ title,id }) =gt; (  lt;View style={styles.item}gt;  lt;Text style={styles.title}gt;{title}lt;/Textgt;  lt;TouchableOpacity onPress={onRemove(id)}gt;  lt;Image style={{width:30,height:30}}  source={require('../assets/delete.png')}  /gt;  lt;/TouchableOpacitygt;  lt;/Viewgt;  );   useEffect(() =gt; {    return () =gt; {  setRenderData({});  }; }, []);   const onImageLibraryPress = useCallback(() =gt; {    const options = {  selectionLimit: 1,  mediaType: 'photo',  includeBase64: false,  };  ImagePicker.launchImageLibrary(options, setPickerResponse);  }, []);   const onCameraPress = useCallback(() =gt; {  const options = {  saveToPhotos: true,  mediaType: 'photo',  includeBase64: false,  maxWidth: 500,  maxHeight: 500,  quality: 0.5,  };  ImagePicker.launchCamera(options, setPickerResponse);  }, []);   const handleUploadPhoto = () =gt; {  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) =gt; {    setRenderData(res.data);     console.log(‘RESULT: ',renderData);     })  .catch((err) =gt; {  console.log('error', err);  })  };   const uri = pickerResponse?.assets amp;amp; pickerResponse.assets[0].uri;   if(uri!==undefined){     handleUploadPhoto();  }   const renderItem = ({ item }) =gt; (  lt;Item title={item.Text}  id = {item.Id}  /gt;  );  return (  lt;View style={styles.screen}gt;    lt;ImagePickerAvatar uri={uri} onPress={() =gt; setVisible(true)} /gt;   lt;FlatList style={styles.usernameText}  data={renderData}  renderItem={renderItem}  keyExtractor={item =gt; item.Id}  /gt;    lt;ImagePickerModal  isVisible={visible}  onClose={() =gt; setVisible(false)}  onImageLibraryPress={onImageLibraryPress}  onCameraPress={onCameraPress}  /gt;    lt;/Viewgt;  ); }  });  

Ответ №1:

Ти Джей Краудер решил вашу проблему с помощью повторного рендеринга. Вторая проблема заключается в этой части вашего кода:

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

Я не думаю id =gt; e=gt; {} , что это допустимый синтаксис, он должен быть таким:

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

Я нашел еще одну причину, по которой ты продолжаешь звонить setRenderData . У вас есть setRenderData в этой функции :

 onst handleUploadPhoto = () =gt; {  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) =gt; {   setRenderData(res.data);    console.log(‘RESULT: ',renderData);    })  .catch((err) =gt; {  console.log('error', err);  }) };  

и каждый раз, когда вы повторно визуализируете его, снова вызывайте эту строку. Если uri нет undefined , то он будет звонить handleUploadPhoto снова и снова.

 const uri = pickerResponse?.assets amp;amp; pickerResponse.assets[0].uri;  if(uri!==undefined){  handleUploadPhoto(); }  

Мое предложение состоит в том, чтобы сделать это так:

 useEffect(()=gt;{  const uri = pickerResponse?.assets amp;amp; pickerResponse.assets[0].uri;   if(uri!==undefined){  handleUploadPhoto();  } },[pickerResponse])  

В приведенном выше коде мы помещаем коды, вызывающие проблему, в a useEffect , чтобы она вызывалась только pickerResponse при каждом изменении, вместо того, чтобы вызывать их при каждом рендеринге

Комментарии:

1. Спасибо. Функция удаления вызывается сейчас, но эта строка setRenderData(res.data) по-прежнему вызывается несколько раз, поэтому даже после удаления список заполняется исходным списком.

2. Я просто добавляю новый ответ к своему сообщению, проверьте его

3. Хорошо, тогда как или где я могу вызвать метод handleUploadPhoto ()?

4. Я просто отредактировал его еще раз, см. Последнюю часть для моего предложения

Ответ №2:

Это не проблема, которая setRenderData вызывается повторно, но в вашем коде есть две проблемы:

  1. onPress={onRemove(id)} должно быть onPress={() =gt; onRemove(id)} . Вам нужно предоставить функцию в качестве onPress свойства, а не результата вызова функции.
  2. Всякий раз, когда вы устанавливаете новое состояние на основе существующего состояния, рекомендуется (и часто необходимо) использовать версию функции обратного вызова, чтобы вы работали в актуальном состоянии. Вы передаете функцию, которая получает актуальное состояние:
     setRenderData(renderData =gt; renderData.filter(  renderData =gt; renderData.Id !== id ));  

Примечание: Часть вашего кода должна renderData быть массивом, но другая часть вашего кода делает setRenderData({}) это , что сделает его объектом, не являющимся массивом. Бывает, что часть обратного вызова очистки a useEffect без зависимостей, поэтому этот объект не будет использоваться компонентом (потому что эта очистка выполняется только при отключении компонента), но это все еще не лучшая практика. (В этом также нет необходимости, все состояние освобождается, когда компонент размонтирован.)

Комментарии:

1. Спасибо, что ответили. Я внес оба изменения в свой код. После того, как я внес первое изменение, удаление не работает, я имею в виду, что функция не вызывается, когда я проверял журналы. lt;Сенсорный экран onPress={()=gt;onRemove(id)}lt;Сенсорный экран onPress={()=gt;gt; lt;Сенсорный экран onPress={()=gt;gt;lt;Стиль изображения={{ширина:30,высота:30}} источник={требуется(‘../активы/удалить.png’)} / gt; lt;Стиль изображения={{ширина:30,высота:30}} источник={требуется(‘../активы/удалить.png’)} /gt;lt;/Сенсорный экранgt;

2. @spykeburn — Значит, происходит что-то еще . onPress={() =gt; onRemove(id)} это правильно.

3. неправильно ли написана моя функция onRemove? потому что он работал с предыдущей строкой, которая была onPress{onRemove(id)}