我的 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
不是问题,但是你的代码有两个问题:
onPress={onRemove(id)}
应该是 onPress={() => onRemove(id)}
。您需要提供函数作为 onPress
属性,而不是调用函数的 结果 。
每当您根据现有状态设置新状态时,最好的做法(通常是 必要的 做法)使用函数的回调版本,因此您正在以最新状态运行。您传入接收最新状态的函数:
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
更改时调用,而不是在每次渲染时调用它们
我是 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
不是问题,但是你的代码有两个问题:
onPress={onRemove(id)}
应该是onPress={() => onRemove(id)}
。您需要提供函数作为onPress
属性,而不是调用函数的 结果 。每当您根据现有状态设置新状态时,最好的做法(通常是 必要的 做法)使用函数的回调版本,因此您正在以最新状态运行。您传入接收最新状态的函数:
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
更改时调用,而不是在每次渲染时调用它们