从状态中删除项目后平面列表不重新渲染
Flatlist not re-rendering after deleting an item from the state
我正在开发一个 React Native 应用程序,
在这个应用程序中,我有一个捕获视图的图像,如果用户捕获图像,它将存储在状态挂钩中,如果存储了一些东西,它将显示在 Flatlist 中。
如果我们认为用户需要从列表中删除一个项目,那我提供了一个删除按钮。当用户点击它时,该项目应该从状态中删除并更新 Flatlist。
我的问题是:从状态中删除项目后,我的视图不会重新呈现。我可以保证数据成功从状态中删除,但我的 Flatlist 没有更新。
我的代码如下,请帮我找到答案。提前致谢。
以下代码用于从状态中删除项目。
const [selectedFiles, setSelectedFiles] = useState([]);
const removeItemFromArray = (index: number) => {
let imagesArray = selectedFiles;
imagesArray.splice(index, 1);
setSelectedFiles(imagesArray);
}
平面列表
<FlatList
showsHorizontalScrollIndicator={false}
data={selectedFiles}
renderItem={({ item, index }) => {
return (
<ImagePreviewSlider
itemData={item}
viewImage={() => viewImage(index)}
deleteItem={() => removeItemFromArray(index)}
/>
);
}}
/>
------------完整代码-------------
photoUpload.tsx
import React, { useState } from "react";
import {
View,
Text,
StyleSheet,
ScrollView,
Image,
ImageBackground,
TouchableOpacity,
Modal,
FlatList
} from "react-native";
import ImagePicker from 'react-native-image-picker/lib/commonjs';
import ComponentStyles, { FONT_FAMILY, COLORS } from "../../../../constants/Component.styles";
import IconF from 'react-native-vector-icons/FontAwesome';
import ActionButton from "../../../../components/ActionButton";
import ImagePreviewSlider from "../../../../subComponents/ProgressBarWithImage";
import InspectionCheckListItem from './InspectionCheckListRow';
const ImageUpload = props => {
/**
* image capturing and upload tab view
*/
const [modalVisible, setModalVisible] = useState(false);
const [selectedFiles, setSelectedFiles] = useState([]);
/**
* used to open popup dialog / state hook will be updated.
*/
const openModal = () => {
// setModalVisible(true);
props.navigation.navigate('Inspection result');
}
/**
* when user click on previous button this method will be worked.
*/
const previousTab = () => {
props.navigation.navigate('Inspection checkList');
}
/**
* below method used to close the popup dialog.
*/
const colseModal = () => {
setModalVisible(false);
}
/**
* @chooseFile method is a alert dialog / here user can select camera or galery
* @ImagePicker method used to open camera and collect picture / select picture from galery(function)
*/
const chooseFile = () => {
const options = {
title: 'Select an option',
storageOptions: {
skipBackup: true,
path: 'images',
},
};
ImagePicker.showImagePicker(options, (response) => {
// console.log('Response = ', response);
if (response.didCancel) {
console.log('User cancelled image picker');
} else if (response.error) {
console.log('ImagePicker Error: ', response.error);
} else {
// let source = response;
// You can also display the image using data:
let source = {
uri: 'data:image/jpeg;base64,' + response.data
};
setImagesToHooks(source);
}
});
};
/**
*
* @param newImage base64- converted image
*/
const setImagesToHooks = (newImage) => {
// This will update the array. Refer the blog link for more information.
let imagesArray = [...selectedFiles, newImage];
setSelectedFiles(imagesArray);
};
const removeItemFromArray = (index: number) => {
let imagesArray = selectedFiles;
imagesArray.splice(index, 1);
setSelectedFiles(imagesArray);
}
const viewImage = (index: number) => {
console.log("view item : ######## : ");
}
return (
<View style={{ backgroundColor: COLORS.WHITE_BG, flex: 1, borderTopLeftRadius: 30, borderTopRightRadius: 30 }}>
<View style={{ flexDirection: 'row', width: '100%', height: '90%' }}>
<View style={{ width: '50%', height: '100%', padding: "2%" }}>
<TouchableOpacity style={{ width: '100%', height: 300, alignItems: 'center', justifyContent: 'center' }} onPress={chooseFile}>
<ImageBackground style={{ width: '100%', height: 300, alignItems: 'center', justifyContent: 'center' }} resizeMode={'stretch'}
source={require('../../../../assets/images/Rectangle_image_upload.png')} >
<IconF style={{ color: COLORS.ASH_AE }} name="camera" size={80} />
<Text style={{ color: COLORS.ASH_AE }}>Take image or upload from device</Text>
</ImageBackground>
</TouchableOpacity>
</View>
<View style={{ width: '50%', marginTop: 5, }}>
{/* <ProgressBar array={selectedFiles} deleteItem={(value) => deleteItemFromArray(value)}/> */}
<FlatList
showsHorizontalScrollIndicator={false}
data={selectedFiles}
renderItem={({ item, index }) => {
return (
<ImagePreviewSlider
itemData={item}
viewImage={() => viewImage(index)}
deleteItem={() => removeItemFromArray(index)}
/>
);
}}
/>
</View>
</View>
<View style={styles.actionButton}>
<ActionButton
title={'Previous'}
color={COLORS.PINK}
customBtnStyle={{
height: 65,
width: '85%',
}}
onPress={() => previousTab()}
/>
<ActionButton
title={'Next'}
color={COLORS.GREEN_42}
customBtnStyle={{
height: 65,
width: '100%',
}}
onPress={() => openModal()}
/>
</View>
<View>
<Modal
animationType="fade"
transparent={true}
visible={modalVisible} >
<View style={styles.modalContainer}>
<View style={styles.centeredView}>
<View style={{ flexDirection: 'row', alignItems: 'center' }}>
<TouchableOpacity style={{ flex: 0.2 }} onPress={colseModal}>
<Image source={require('../../../../assets/images/ic-close.png')} style={{ height: 20, width: 20, marginRight: 10 }} />
</TouchableOpacity>
</View>
<View style={styles.columnView}>
<Text style={styles.contentTitle}>Inspection of pharmacies</Text>
<Text style={styles.contentSubTitle}>Checklist:</Text>
<View style={styles.rowView}>
<View style={{ flex: 1, alignSelf: 'center' }} >
<Text style={styles.tableText}>Description</Text>
</View>
<View style={{ flex: 1, alignSelf: 'center' }} >
<Text style={styles.tableText}>Validiry</Text>
</View>
<View style={{ flex: 1, alignSelf: 'center' }} >
<Text style={styles.tableText}>Remark</Text>
</View>
</View>
</View>
<View style={{ flex: 2, width: '100%', flexDirection: 'column' }}>
{/* <InspectionCheckListItem array={InspectionList.items} /> */}
</View>
</View>
</View>
</Modal>
</View>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: 'center',
justifyContent: 'center'
},
modalContainer: {
position: 'absolute',
width: '100%',
height: '100%',
justifyContent: 'center',
alignItems: 'center',
backgroundColor: 'rgba(100,100,100, 0.5)',
padding: 20,
},
centeredView: {
position: 'relative',
width: '90%',
height: '90%',
backgroundColor: COLORS.WHITE_FC,
padding: 20,
},
inspectionNumber: {
flex: 1.5,
fontSize: 18,
color: COLORS.BLUE_69,
fontFamily: FONT_FAMILY.BOLD
},
modalTitle: {
flex: 2,
fontSize: 12,
color: COLORS.GREEN_42,
fontFamily: FONT_FAMILY.BOLD
},
contentTitle: {
fontSize: 18,
color: COLORS.BLUE_69,
fontFamily: FONT_FAMILY.REGULAR,
},
contentSubTitle: {
fontSize: 18,
color: COLORS.BLUE_69,
fontFamily: FONT_FAMILY.BOLD,
},
columnView: {
flexDirection: 'column',
justifyContent: 'center',
alignItems: 'center'
},
rowView: {
flexDirection: 'row',
justifyContent: 'center',
alignItems: 'center',
marginTop: '5%'
},
tableText: {
fontSize: 18,
color: COLORS.BLUE_69,
fontFamily: FONT_FAMILY.LIGHT,
justifyContent: 'center',
alignSelf: 'center'
},
actionButton: {
flex: 1,
flexDirection: 'row',
justifyContent: 'center',
alignItems: 'flex-end',
position: 'relative',
bottom: '2%',
left: 0,
}
});
export default ImageUpload;
ProgressBarWithImage.tsx
import React, { Component } from "react";
import {
View,
Text,
StyleSheet,
TouchableOpacity
} from "react-native";
import IconMC from 'react-native-vector-icons/MaterialCommunityIcons';
import { ProgressBar } from '@react-native-community/progress-bar-android';
import { COLORS, FONT_FAMILY } from "../constants/Component.styles";
const ProgressBarWithImage = props => {
return (
<View style={styles.container}>
<View style={{ justifyContent: 'center', alignItems: 'center', backgroundColor: COLORS.PINK, opacity: 0.3, height: 40, width: 40, borderRadius: 100 }}>
<TouchableOpacity onPress={props.viewImage}>
<IconMC style={{ color: COLORS.PINK, opacity: 100 }} name="file-image" size={30} />
</TouchableOpacity>
</View>
<View style={{ marginLeft: 20, }}>
<View style={{ flexDirection: 'row', alignItems: 'center', width: '100%' }}>
<Text style={{ fontSize: 15, color: COLORS.BLUE_2C, fontFamily: FONT_FAMILY.SEMI_BOLD }}>Photo01.PNG</Text>
<View style={{ flex: 1 }} />
<TouchableOpacity onPress={props.deleteItem}>
<IconMC style={{ color: COLORS.ASH_AE, opacity: 100, marginLeft: 60, }} name="close" size={20} />
</TouchableOpacity>
</View>
<Text style={{ fontSize: 15, color: COLORS.ASH_AE, fontFamily: FONT_FAMILY.SEMI_BOLD }}>7.5Mb</Text>
<ProgressBar
styleAttr="Horizontal"
indeterminate={false}
progress={1}
/>
</View>
</View>
);
};
const styles = StyleSheet.create({
container: {
alignItems: 'center',
justifyContent: 'center',
flexDirection: 'row',
width: '100%',
marginTop: 20,
},
});
export default ProgressBarWithImage;
只需使用 flatList 的 extraData 属性重新渲染 flatList。
只需添加此状态
const [refreshFlatlist, setRefreshFlatList] = useState(false);
然后在 removeItemFromArray 函数中添加以下行
setRefreshFlatList(!refreshFlatlist)
最后,在 flatList 中添加这个属性
extraData(refreshFlatlist)
我正在开发一个 React Native 应用程序,
在这个应用程序中,我有一个捕获视图的图像,如果用户捕获图像,它将存储在状态挂钩中,如果存储了一些东西,它将显示在 Flatlist 中。
如果我们认为用户需要从列表中删除一个项目,那我提供了一个删除按钮。当用户点击它时,该项目应该从状态中删除并更新 Flatlist。
我的问题是:从状态中删除项目后,我的视图不会重新呈现。我可以保证数据成功从状态中删除,但我的 Flatlist 没有更新。
我的代码如下,请帮我找到答案。提前致谢。
以下代码用于从状态中删除项目。
const [selectedFiles, setSelectedFiles] = useState([]);
const removeItemFromArray = (index: number) => {
let imagesArray = selectedFiles;
imagesArray.splice(index, 1);
setSelectedFiles(imagesArray);
}
平面列表
<FlatList
showsHorizontalScrollIndicator={false}
data={selectedFiles}
renderItem={({ item, index }) => {
return (
<ImagePreviewSlider
itemData={item}
viewImage={() => viewImage(index)}
deleteItem={() => removeItemFromArray(index)}
/>
);
}}
/>
------------完整代码-------------
photoUpload.tsx
import React, { useState } from "react";
import {
View,
Text,
StyleSheet,
ScrollView,
Image,
ImageBackground,
TouchableOpacity,
Modal,
FlatList
} from "react-native";
import ImagePicker from 'react-native-image-picker/lib/commonjs';
import ComponentStyles, { FONT_FAMILY, COLORS } from "../../../../constants/Component.styles";
import IconF from 'react-native-vector-icons/FontAwesome';
import ActionButton from "../../../../components/ActionButton";
import ImagePreviewSlider from "../../../../subComponents/ProgressBarWithImage";
import InspectionCheckListItem from './InspectionCheckListRow';
const ImageUpload = props => {
/**
* image capturing and upload tab view
*/
const [modalVisible, setModalVisible] = useState(false);
const [selectedFiles, setSelectedFiles] = useState([]);
/**
* used to open popup dialog / state hook will be updated.
*/
const openModal = () => {
// setModalVisible(true);
props.navigation.navigate('Inspection result');
}
/**
* when user click on previous button this method will be worked.
*/
const previousTab = () => {
props.navigation.navigate('Inspection checkList');
}
/**
* below method used to close the popup dialog.
*/
const colseModal = () => {
setModalVisible(false);
}
/**
* @chooseFile method is a alert dialog / here user can select camera or galery
* @ImagePicker method used to open camera and collect picture / select picture from galery(function)
*/
const chooseFile = () => {
const options = {
title: 'Select an option',
storageOptions: {
skipBackup: true,
path: 'images',
},
};
ImagePicker.showImagePicker(options, (response) => {
// console.log('Response = ', response);
if (response.didCancel) {
console.log('User cancelled image picker');
} else if (response.error) {
console.log('ImagePicker Error: ', response.error);
} else {
// let source = response;
// You can also display the image using data:
let source = {
uri: 'data:image/jpeg;base64,' + response.data
};
setImagesToHooks(source);
}
});
};
/**
*
* @param newImage base64- converted image
*/
const setImagesToHooks = (newImage) => {
// This will update the array. Refer the blog link for more information.
let imagesArray = [...selectedFiles, newImage];
setSelectedFiles(imagesArray);
};
const removeItemFromArray = (index: number) => {
let imagesArray = selectedFiles;
imagesArray.splice(index, 1);
setSelectedFiles(imagesArray);
}
const viewImage = (index: number) => {
console.log("view item : ######## : ");
}
return (
<View style={{ backgroundColor: COLORS.WHITE_BG, flex: 1, borderTopLeftRadius: 30, borderTopRightRadius: 30 }}>
<View style={{ flexDirection: 'row', width: '100%', height: '90%' }}>
<View style={{ width: '50%', height: '100%', padding: "2%" }}>
<TouchableOpacity style={{ width: '100%', height: 300, alignItems: 'center', justifyContent: 'center' }} onPress={chooseFile}>
<ImageBackground style={{ width: '100%', height: 300, alignItems: 'center', justifyContent: 'center' }} resizeMode={'stretch'}
source={require('../../../../assets/images/Rectangle_image_upload.png')} >
<IconF style={{ color: COLORS.ASH_AE }} name="camera" size={80} />
<Text style={{ color: COLORS.ASH_AE }}>Take image or upload from device</Text>
</ImageBackground>
</TouchableOpacity>
</View>
<View style={{ width: '50%', marginTop: 5, }}>
{/* <ProgressBar array={selectedFiles} deleteItem={(value) => deleteItemFromArray(value)}/> */}
<FlatList
showsHorizontalScrollIndicator={false}
data={selectedFiles}
renderItem={({ item, index }) => {
return (
<ImagePreviewSlider
itemData={item}
viewImage={() => viewImage(index)}
deleteItem={() => removeItemFromArray(index)}
/>
);
}}
/>
</View>
</View>
<View style={styles.actionButton}>
<ActionButton
title={'Previous'}
color={COLORS.PINK}
customBtnStyle={{
height: 65,
width: '85%',
}}
onPress={() => previousTab()}
/>
<ActionButton
title={'Next'}
color={COLORS.GREEN_42}
customBtnStyle={{
height: 65,
width: '100%',
}}
onPress={() => openModal()}
/>
</View>
<View>
<Modal
animationType="fade"
transparent={true}
visible={modalVisible} >
<View style={styles.modalContainer}>
<View style={styles.centeredView}>
<View style={{ flexDirection: 'row', alignItems: 'center' }}>
<TouchableOpacity style={{ flex: 0.2 }} onPress={colseModal}>
<Image source={require('../../../../assets/images/ic-close.png')} style={{ height: 20, width: 20, marginRight: 10 }} />
</TouchableOpacity>
</View>
<View style={styles.columnView}>
<Text style={styles.contentTitle}>Inspection of pharmacies</Text>
<Text style={styles.contentSubTitle}>Checklist:</Text>
<View style={styles.rowView}>
<View style={{ flex: 1, alignSelf: 'center' }} >
<Text style={styles.tableText}>Description</Text>
</View>
<View style={{ flex: 1, alignSelf: 'center' }} >
<Text style={styles.tableText}>Validiry</Text>
</View>
<View style={{ flex: 1, alignSelf: 'center' }} >
<Text style={styles.tableText}>Remark</Text>
</View>
</View>
</View>
<View style={{ flex: 2, width: '100%', flexDirection: 'column' }}>
{/* <InspectionCheckListItem array={InspectionList.items} /> */}
</View>
</View>
</View>
</Modal>
</View>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: 'center',
justifyContent: 'center'
},
modalContainer: {
position: 'absolute',
width: '100%',
height: '100%',
justifyContent: 'center',
alignItems: 'center',
backgroundColor: 'rgba(100,100,100, 0.5)',
padding: 20,
},
centeredView: {
position: 'relative',
width: '90%',
height: '90%',
backgroundColor: COLORS.WHITE_FC,
padding: 20,
},
inspectionNumber: {
flex: 1.5,
fontSize: 18,
color: COLORS.BLUE_69,
fontFamily: FONT_FAMILY.BOLD
},
modalTitle: {
flex: 2,
fontSize: 12,
color: COLORS.GREEN_42,
fontFamily: FONT_FAMILY.BOLD
},
contentTitle: {
fontSize: 18,
color: COLORS.BLUE_69,
fontFamily: FONT_FAMILY.REGULAR,
},
contentSubTitle: {
fontSize: 18,
color: COLORS.BLUE_69,
fontFamily: FONT_FAMILY.BOLD,
},
columnView: {
flexDirection: 'column',
justifyContent: 'center',
alignItems: 'center'
},
rowView: {
flexDirection: 'row',
justifyContent: 'center',
alignItems: 'center',
marginTop: '5%'
},
tableText: {
fontSize: 18,
color: COLORS.BLUE_69,
fontFamily: FONT_FAMILY.LIGHT,
justifyContent: 'center',
alignSelf: 'center'
},
actionButton: {
flex: 1,
flexDirection: 'row',
justifyContent: 'center',
alignItems: 'flex-end',
position: 'relative',
bottom: '2%',
left: 0,
}
});
export default ImageUpload;
ProgressBarWithImage.tsx
import React, { Component } from "react";
import {
View,
Text,
StyleSheet,
TouchableOpacity
} from "react-native";
import IconMC from 'react-native-vector-icons/MaterialCommunityIcons';
import { ProgressBar } from '@react-native-community/progress-bar-android';
import { COLORS, FONT_FAMILY } from "../constants/Component.styles";
const ProgressBarWithImage = props => {
return (
<View style={styles.container}>
<View style={{ justifyContent: 'center', alignItems: 'center', backgroundColor: COLORS.PINK, opacity: 0.3, height: 40, width: 40, borderRadius: 100 }}>
<TouchableOpacity onPress={props.viewImage}>
<IconMC style={{ color: COLORS.PINK, opacity: 100 }} name="file-image" size={30} />
</TouchableOpacity>
</View>
<View style={{ marginLeft: 20, }}>
<View style={{ flexDirection: 'row', alignItems: 'center', width: '100%' }}>
<Text style={{ fontSize: 15, color: COLORS.BLUE_2C, fontFamily: FONT_FAMILY.SEMI_BOLD }}>Photo01.PNG</Text>
<View style={{ flex: 1 }} />
<TouchableOpacity onPress={props.deleteItem}>
<IconMC style={{ color: COLORS.ASH_AE, opacity: 100, marginLeft: 60, }} name="close" size={20} />
</TouchableOpacity>
</View>
<Text style={{ fontSize: 15, color: COLORS.ASH_AE, fontFamily: FONT_FAMILY.SEMI_BOLD }}>7.5Mb</Text>
<ProgressBar
styleAttr="Horizontal"
indeterminate={false}
progress={1}
/>
</View>
</View>
);
};
const styles = StyleSheet.create({
container: {
alignItems: 'center',
justifyContent: 'center',
flexDirection: 'row',
width: '100%',
marginTop: 20,
},
});
export default ProgressBarWithImage;
只需使用 flatList 的 extraData 属性重新渲染 flatList。
只需添加此状态
const [refreshFlatlist, setRefreshFlatList] = useState(false);
然后在 removeItemFromArray 函数中添加以下行
setRefreshFlatList(!refreshFlatlist)
最后,在 flatList 中添加这个属性
extraData(refreshFlatlist)