Удаление элемента из массива в состоянии компонента

119

Я пытаюсь найти лучший способ удалить элемент из массива в состоянии компонента. Так как я не должен изменять переменную this.state напрямую, есть ли лучший (более краткий) способ удалить элемент из массива, чем тот, который у меня есть здесь:

  onRemovePerson: function(index) {
    this.setState(prevState => { // pass callback in setState to avoid race condition
      let newData = prevState.data.slice() //copy array from prevState
      newData.splice(index, 1) // remove element
      return {data: newData} // update state
    })
  },

Спасибо.

обновленный

Это было обновлено для использования обратного вызова в setState. Это должно быть сделано при обращении к текущему состоянию при его обновлении.

  • 0
    Посмотрите на ImmutableJS из Facebook, который хорошо работает с React. ссылка на сайт
  • 6
    Я не вижу ничего плохого в вашем коде. На самом деле это очень идиоматичный способ сделать это.
Теги:

10 ответов

122
Лучший ответ

Самый чистый способ сделать это, который я видел, - это filter:

removeItem(index) {
  this.setState({
    data: this.state.data.filter((_, i) => i !== index)
  });
}
  • 7
    работает! Не могли бы вы объяснить, для чего _?
  • 14
    @HussienK _ иногда используется для обозначения неиспользованного аргумента. Здесь это текущий элемент в массиве.
Показать ещё 6 комментариев
85

Вы можете использовать update() помощник по неизменяемости из react-addons-update, который эффективно делает то же самое под капотом, но то, что вы делаете, хорошо.

this.setState(prevState => ({
  data: update(prevState.data, {$splice: [[index, 1]]})
}))
  • 0
    Гораздо более простой пример, чем тот, на который вы ссылались :)
  • 6
    react-addons-update теперь устарела (2016). immutability-helper доступен в качестве замены. github.com/kolodny/immutability-helper Также, пожалуйста, смотрите мой ответ ниже о том, чтобы не изменять му. this.state непосредственно внутри this.setState ().
51

Я считаю, что привязка this.state внутри setState() обескуражена https://facebook.github.io/react/docs/state-and-lifecycle.html#state-updates-may-be-asynchronous

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

Использование Array.prototype.filter без ES6

removeItem : function(index) {
  this.setState(function(prevState){
    return { data : prevState.data.filter(function(val, i) {
      return i !== index;
    })};
  });
}

Использование Array.prototype.filter с функциями ES6 Arrow

removeItem(index) {
  this.setState((prevState) => ({
    data: prevState.data.filter((_, i) => i !== index)
  }));
}

Использование универсального помощника

import update from 'immutability-helper'
...
removeItem(index) {
  this.setState((prevState) => ({
    data: update(prevState.data, {$splice: [[index, 1]]})
  }))
}

Использование Spread

function removeItem(index) {
  this.setState((prevState) => ({
    data: [...prevState.data.slice(0,index), ...prevState.data.slice(index+1)]
  }))
}

Обратите внимание, что в каждом случае, независимо от используемого метода, this.setState() передается обратный вызов, а не ссылка на объект old this.state;

  • 3
    Это правильный ответ, см. Также Сила не мутации данных
  • 1
    Примеры использования обратного вызова prevState с добавлением различных методов.
Показать ещё 3 комментария
22

Вот способ удаления элемента из массива в состоянии с использованием синтаксиса распространения ES6.

onRemovePerson: (index) => {
  const data = this.state.data;
  this.setState({ 
    data: [...data.slice(0,index), ...data.slice(index+1)]
  });
}
  • 1
    Спасибо! Это похоже на самый естественный способ сделать это.
2

Я хочу присоединиться здесь, даже несмотря на то, что @pscl уже правильно ответил на этот вопрос, если кто-то столкнется с той же проблемой, что и я. Из 4 методов, которые я дал, я решил использовать синтаксис es6 с функциями стрелок из-за его краткости и отсутствия зависимости от внешних библиотек:

Использование Array.prototype.filter с функциями стрелок ES6

removeItem(index) {
  this.setState((prevState) => ({
    data: prevState.data.filter((_, i) => i != index)
  }));
}

Как вы можете видеть, я сделал небольшую модификацию, чтобы игнорировать тип индекса (!== to !=), Потому что в моем случае я извлекал индекс из строкового поля.

Еще один полезный момент, если вы видите странное поведение при удалении элемента на стороне клиента, это НИКОГДА не использовать индекс массива в качестве ключа для элемента:

// bad
{content.map((content, index) =>
  <p key={index}>{content.Content}</p>
)}

Когда React отличается от виртуального DOM при изменении, он просматривает ключи, чтобы определить, что изменилось. Поэтому, если вы используете индексы, а в массиве их на единицу меньше, последний удалит последний. Вместо этого используйте идентификатор содержимого в качестве ключей, как это.

// good
{content.map(content =>
  <p key={content.id}>{content.Content}</p>
)}

Выше приведен отрывок из этого ответа из соответствующего поста.

Счастливого кодирования всем!

1

Вы можете использовать эту функцию, если хотите удалить элемент (без индекса)

removeItem(item) {
  this.setState(prevState => {
    data: prevState.data.filter(i => i !== item)
  });
}
1

Как упомянуто в комментарии к ответу ephrion выше, filter() может быть медленным, особенно с большими массивами, так как он циклически ищет индекс, который, по-видимому, уже определен. Это чистое, но неэффективное решение.

В качестве альтернативы можно просто "нарезать" нужный элемент и объединить фрагменты.

var dummyArray = [];    
this.setState({data: dummyArray.concat(this.state.data.slice(0, index), this.state.data.slice(index))})

Надеюсь это поможет!

0

Вы можете сделать код более читабельным с помощью однострочной вспомогательной функции:

const removeElement = (arr, i) => [...arr.slice(0, i), ...arr.slice(i+1)];

тогда используйте это так:

this.setState(state => ({ places: removeElement(state.places, index) }));
-1

Я использую это, надеюсь, что это не проблема и помощь.

removeItem(item){
    const index = this.state.yourArray.indexOf(item);
    if (index < 0) return
    this.state.yourArray.splice(index, 1)
    this.setState({yourArray: this.state.yourArray})
}
  • 0
    Проблема с этим подходом заключается в том, что вы this.state напрямую. Вы должны сделать копию this.state.yourArray прежде чем this.state.yourArray его.
-3

Вот простой способ сделать это:

removeFunction(key){
  const data = {...this.state.data}; //Duplicate state.
  delete data[key];                  //remove Item form stateCopy.
  this.setState({data});             //Set state as the modify one.
}

Надеюсь, что это поможет!!!

  • 1
    почему за это проголосовали?
  • 0
    Хотите объяснить?
Показать ещё 1 комментарий

Ещё вопросы

Сообщество Overcoder
Наверх
Меню