Pasindu Weerakoon
Pasindu Weerakoon

Reputation: 628

Flatlist not re-rendering after deleting an item from the state

I'm working on a react native application,

In this application I have an image capturing the view, If the user captures an image it will be stored in the state hook, if something stored it will be displayed in Flatlist.

If we think the user needs to remove an item from the list, that I have provided a delete button. When a user clicks on it the item should be removed from the state and update the Flatlist.

My question is: after removing an item from the state my view is not re-render. I can guarantee that data is successfully removed from the state, but my Flatlist not updating.

My code as follows, please help me to find an answer. Thanks in advance.

below code used to remove the item from the state.

const [selectedFiles, setSelectedFiles] = useState([]);

const removeItemFromArray = (index: number) => {
     let imagesArray = selectedFiles;
     imagesArray.splice(index, 1);
     setSelectedFiles(imagesArray);
}

flatlist

<FlatList
 showsHorizontalScrollIndicator={false}
                    data={selectedFiles}
                    renderItem={({ item, index }) => {
                        return (
                            <ImagePreviewSlider
                                itemData={item}
                                viewImage={() => viewImage(index)}
                                deleteItem={() => removeItemFromArray(index)}
                            />
                        );
                    }}
                />

---------- complete code --------------

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;

Upvotes: 3

Views: 3838

Answers (1)

rishikesh_07
rishikesh_07

Reputation: 1039

Just use extraData prop of flatList to re-render flatList.

Simply add this state

const [refreshFlatlist, setRefreshFlatList] = useState(false);

And on in removeItemFromArray function add the following line

setRefreshFlatList(!refreshFlatlist)

finally, in flatList add this prop

extraData(refreshFlatlist)

Upvotes: 7

Related Questions