Реагировать на нативное удаление нескольких элементов из массива состояний

У меня есть каталог, в котором хранятся изображения, сделанные с помощью камеры. Для сохранения изображений я использую RNFS. Я использую react-native-photo-browser.

В самой галерее нет возможности удалить элементы из галереи. Так что я работаю, чтобы достичь этого

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>
        )
    }
}

Пример списка изображений:

[
    {
     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',
      },
  ]

Моя цель - всякий раз, когда элемент из состояния galleryList удаляется, мне нужно обновить компонент, поэтому удаленное изображение будет удалено из галереи. Итак, когда я пытаюсь использовать фильтр galleryList, он удаляет другие изображения вместо других изображений:

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

MCVE -> Это уменьшенная версия моего кода, вы можете видеть, что изображения удаляются случайным образом


person Jothi Kannan    schedule 18.12.2018    source источник
comment
Так что же происходит на самом деле?   -  person agenthunt    schedule 18.12.2018
comment
Он удаляет изображения из каталога, но из пользовательского интерфейса удаляет случайные изображения.   -  person Jothi Kannan    schedule 19.12.2018
comment
this.state.mediaSelected.map действительно должен быть this.state.mediaSelected.forEach?   -  person Drew Reese    schedule 19.12.2018
comment
@DrewReese нет, forEach тоже работает так же   -  person Jothi Kannan    schedule 19.12.2018
comment
Что ж, map возвращает новый массив с тем же количеством элементов (которое ваш код не захватывает), а forEach вызывает функцию для каждого элемента. Как вы его используете, конечно, делаете то же самое и работаете одинаково, поскольку вы никогда не возвращаете новое значение для сопоставления. Просто указал, что ваш код, вероятно, должен использовать вместо этого forEach.   -  person Drew Reese    schedule 19.12.2018
comment
Вероятно, у вас есть компонент, который отвечает за список элементов, попробуйте добавить ключевой атрибут к этому элементу. Что-то вроде этого: this.state.galleryList.map(item =› ‹ItemComponent key={item.photo} ...›) Я предполагаю, что эта логика находится в компоненте PhotoBrowser, убедитесь, что вы указали уникальный ключ. Надеюсь, поможет   -  person Leonardo Lobato    schedule 26.12.2018
comment
@LeonardoLobato ключевой атрибут уже присутствует в ListView   -  person Jothi Kannan    schedule 26.12.2018


Ответы (1)


Проблема

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

Поскольку setState является асинхронным, this.state.galleryList не будет обновляться при каждой итерации вашей функции map, поэтому в окончательном обновленном состоянии будет отфильтрован только один элемент вместо всех выбранных элементов.

Решение

Вы можете использовать версию обратного вызова setState, которая вместо этого использует обновленное состояние:

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

Альтернативное решение

Вместо того, чтобы вызывать setState в каждой итерации, вы можете вместо этого вызывать его вне вашей функции map (хотя обновления setState в любом случае будут пакетными, поэтому значительного улучшения производительности не будет):

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

Другие проблемы с вашим кодом

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

Здесь вы напрямую изменяете свое состояние. Сделайте это вместо этого:

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

this.setState(prevState => ({ 
  mediaSelected: prevState.mediaSelected.filter(e => e != media.photo) 
})); 
person Roy Wang    schedule 24.12.2018
comment
Комментарии не для расширенного обсуждения; этот разговор был перенесено в чат. - person Samuel Liew♦; 27.12.2018