4

I have a directory which stores images taken using the camera. For saving images I am using RNFS. I am using react-native-photo-browser.

The gallery itself doesn't have any options to delete the items from the gallery. So I am working to achieve it

export default class GridGallery extends React.Component{

    static navigationOptions = {
      title: 'Image Gallery',
    };

    constructor(props) {
        super(props)
        this.state = {
          filesList : [],
          mediaSelected: [],
          base64URI: null,
          galleryList: []
        }
    }

    componentDidMount(){
        FileList.list((files) => {

            if(files != null) {

                this.fileUrl = files[0].path;

                files = files.sort((a, b) => {

                    if (a.ctime < b.ctime)
                        return 1;
                    if (a.ctime > b.ctime)
                        return -1;
                    return 0;
                });

                this.setState({
                    filesList: files
                });
            }
            console.warn(this.state.filesList);
            this.getFiles();
        });
      }

    getFiles(){
      //console.warn(this.state.filesList);

      const ArrFiles = this.state.filesList.map((file) =>
        ({ caption : file.name, photo : file.path })
      );
      //console.warn(ArrFiles);

      this.setState({ galleryList : ArrFiles });
    }

    onActionButton = (media, index) => {
        if (Platform.OS === 'ios') {
          ActionSheetIOS.showShareActionSheetWithOptions(
            {
              url: media.photo,
              message: media.caption,
            },
            () => {},
            () => {},
          );
        } else {
          alert(`handle sharing on android for ${media.photo}, index: ${index}`);
        }
      };

      handleSelection = async (media, index, isSelected) => {
        if (isSelected == true) {
            this.state.mediaSelected.push(media.photo);
        } else {
         this.state.mediaSelected.splice(this.state.mediaSelected.indexOf(media.photo), 1);
        }
         console.warn(this.state.mediaSelected);
      }

      deleteImageFile = () => {

        const dirPicutures = RNFS.DocumentDirectoryPath;
        //delete mulitple files
        console.warn(this.state.mediaSelected);
        this.state.mediaSelected.map((file) =>
        // filepath = `${dirPicutures}/${file}`
          RNFS.exists(`${file}`)
          .then( (result) => {
              console.warn("file exists: ", result);

              if(result){
                return RNFS.unlink(`${file}`)
                  .then(() => {
                    console.warn('FILE DELETED');
                    let tempgalleryList = this.state.galleryList.filter(item => item.photo !== file);
                    this.setState({ galleryList : tempgalleryList })
                  })
                  // `unlink` will throw an error, if the item to unlink does not exist
                  .catch((err) => {
                    console.warn(err.message);
                  });
              }

            })
            .catch((err) => {
              console.warn(err.message);
            })
        )

     }

     renderDelete(){
       const { galleryList } = this.state;
       if(galleryList.length>0){
         return(
           <View style={styles.topRightContainer}>
           <TouchableOpacity style={{alignItems: 'center',right: 10}} onPress={this.deleteImageFile}>
             <Image
               style={{width: 24, height: 24}}
               source={require('../assets/images/ic_delete.png')}
             />
             </TouchableOpacity>
           </View>
         )
       }
     }

     goBack() {
       const { navigation } = this.props;
       navigation.pop;
     }

    render() {
      const { galleryList } = this.state;
        return (
            <View style={styles.container}>
                <View style={{flex: 1}}>
                <PhotoBrowser
                    mediaList={galleryList}
                    enableGrid={true}
                    displayNavArrows={true}
                    displaySelectionButtons={true}
                    displayActionButton={true}
                    onActionButton={this.onActionButton}
                    displayTopBar = {true}
                    onSelectionChanged={this.handleSelection}
                    startOnGrid={true}
                    initialIndex={0}
                />
                </View>
                {this.renderDelete()}
            </View>
        )
    }
}

An example list of images:

[
    {
     photo:'4072710001_f36316ddc7_b.jpg',
      caption: 'Grotto of the Madonna',
      },
      {
        photo: /media/broadchurch_thumbnail.png,
        caption: 'Broadchurch Scene',
      },
      {
        photo:
          '4052876281_6e068ac860_b.jpg',
          caption: 'Beautiful Eyes',
      },
  ]

My aim is whenever the item from state galleryList is removed I need to refresh the component, so the deleted image will be removed from the gallery. So When I try to use filter the galleryList it deleting other images instead of other images:

let tempgalleryList = this.state.galleryList.filter(item => item.photo !== file);
                    this.setState({ galleryList : tempgalleryList })

MCVE -> This is a minified version of my code, you can see the images are deleting randomly

Jothi Kannan
  • 3,320
  • 6
  • 40
  • 77
  • So what is actually happening? – agenthunt Dec 18 '18 at 20:44
  • It deleting the images from the directory but from UI it deleting random images. – Jothi Kannan Dec 19 '18 at 01:46
  • Is `this.state.mediaSelected.map` supposed to really be `this.state.mediaSelected.forEach`? – Drew Reese Dec 19 '18 at 07:24
  • @DrewReese nope, forEach also working like same way – Jothi Kannan Dec 19 '18 at 07:57
  • Well, `map` returns a new array with the same number of elements (which your code doesn't capture), and `forEach` calls a function for each element. How you're using it is, ofc, doing the same thing and working the same way since you're never returning a new value to map to. Was just pointing out that your code should probably be using the `forEach` instead. – Drew Reese Dec 19 '18 at 08:11
  • Probably you have a component that is responsable for list the items, try to add the key attribute to this element. Something like that: this.state.galleryList.map(item => ) I guess this logic is on PhotoBrowser component, make sure you put a key that is unique. hope it helps – Leonardo Lobato Dec 26 '18 at 15:40
  • @LeonardoLobato there is key attribute already present in the ListView – Jothi Kannan Dec 26 '18 at 18:50

1 Answers1

5

Problem

let tempgalleryList = this.state.galleryList.filter(item => item.photo !== file);
this.setState({ galleryList : tempgalleryList })

Since setState is async, this.state.galleryList will not be updated in each iteration of your map function, so the final updated state will only have one item filtered out instead of all selected items.

Solution

You can use the callback version of setState which uses the updated state instead:

this.setState(prevState => ({ 
  galleryList : prevState.galleryList.filter(item => item.photo !== file), 
}));

Alternative solution

Instead of calling setState in every iteration, you can call it outside of your map function instead (though setState updates will be batched anyway so no significant performance improvement):

this.setState(prevState => ({ 
  galleryList : prevState.galleryList.filter(item => !prevState.mediaSelected.includes(item.photo)), 
}));

Other problems with your code

  this.state.mediaSelected.push(media.photo); 
} else {
  this.state.mediaSelected.splice(this.state.mediaSelected.indexOf(media.photo), 1);

You are directly mutating your state here. Do this instead:

this.setState(prevState => ({ 
  mediaSelected: prevState.mediaSelected.concat(media.photo) 
})); 

this.setState(prevState => ({ 
  mediaSelected: prevState.mediaSelected.filter(e => e != media.photo) 
})); 
Roy Wang
  • 11,112
  • 2
  • 21
  • 42
  • Comments are not for extended discussion; this conversation has been [moved to chat](https://chat.stackoverflow.com/rooms/185830/discussion-on-answer-by-riwu-react-native-delete-multiple-items-from-state-array). – Samuel Liew Dec 27 '18 at 12:01