我的 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=() => onRemove(id)
。您需要提供一个函数作为onPress
属性,而不是调用函数的结果。
每当您基于现有状态设置新状态时,最佳实践(通常是必要实践)使用函数的回调版本,以便您在最高-日期状态。你传入一个接收最新状态的函数:
setRenderData(renderData => renderData.filter(
renderData => renderData.Id !== id
));
旁注:您的部分代码期望 renderData
是一个数组,但您的另一部分代码正在执行 setRenderData()
,这将使其成为非数组对象。碰巧useEffect
的清理回调的一部分没有依赖项,因此组件不会使用该对象(因为仅在卸载组件时才进行清理),但这仍然不是最佳实践。 (也不需要这样做,卸载组件时会释放所有状态。)
【讨论】:
感谢您的回复。我在我的代码中进行了两项更改。在我进行第一次更改后,删除不起作用,我的意思是当我检查日志时该函数没有调用。onPress=() => onRemove(id)
是正确的。
我的 onRemove 函数写错了吗?因为它正在使用上一行 onPressonRemove(id)【参考方案2】:
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
更改时调用,而不是在每次渲染时调用它们
【讨论】:
谢谢。现在调用了删除函数,但这条线 setRenderData(res.data) 仍然被多次调用,因此即使在删除之后,列表也会填充原始列表。 我只是在我的帖子中添加了一个新答案,请查看 好的,那么我应该如何或在哪里调用 handleUploadPhoto() 方法? 我刚刚又编辑了一遍,建议看最后一部分以上是关于我的 setstate 方法被多次调用,这导致在 React Native 中删除项目时出现问题的主要内容,如果未能解决你的问题,请参考以下文章
在 React 组件中多次使用 this.setState 会发生啥?