Видалення елемента з масиву в стані компонента


131

Я намагаюся знайти найкращий спосіб видалити елемент з масиву в стані компонента. Оскільки я не повинен 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. Це слід робити при посиланні на поточний стан під час оновлення.


Погляньте на ImmutableJS з Facebook, який добре працює з React. посилання
Jonatan Lundqvist Medén

6
Я не бачу нічого поганого у вашому коді. Насправді це дуже ідіоматичний спосіб зробити це.
Димитър Димитров

Відповіді:


138

Найчистіший спосіб зробити це, що я бачив, це filter:

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

18
@HussienK the _іноді використовується для представлення невикористаного аргументу. Ось це поточний елемент у масиві.
Христос

1
Дивовижний. Я використовую .filter весь час, але ніколи не вважав його використовувати в цій ситуації. : D
дакт

4
Це чистий код, однак для великих масивів цей метод повільний. З огляду на це, він проходить через весь масив, щоб знайти індекс, який, як видається, вже визначений.
Метт Елліс

1
@MattEllis Оскільки оновлення стану React так чи інакше незмінні, ви введете O (n) у розмір списку, щоб скопіювати його в будь-якому випадку. Я був би здивований, якби це був значний хіт на виставу.
ephrion

4
Не рекомендується оцінювати this.stateяк внесок this.setState(), навіть незмінний. дивіться мою відповідь вище для посилання на офіційні документи React на цю тему.
pscl

87

Ви можете скористатися update()помічником непорушностіreact-addons-update , який ефективно робить те ж саме під кришкою, але те, що ви робите, це добре.

this.setState(prevState => ({
  data: update(prevState.data, {$splice: [[index, 1]]})
}))

Набагато простіший приклад, ніж той, з яким ви пов’язали :)
Коен.

7
react-addons-updateзараз застаріла (2016). immutability-helperдоступний як заміна. github.com/kolodny/immutability-helper Також, будь ласка, дивіться мою відповідь нижче щодо того, щоб не змінювати цей стан.
pscl

62

Я вважаю, що посилання this.stateвсередину на них setState()не рекомендується ( оновлення стану можуть бути асинхронними ).

Документи рекомендують використовувати 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

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()передається зворотний виклик, а не посилання на об'єкт до старого this.state;


3
Це правильна відповідь, див. Також Сила не мутуючих даних
Вінні Джеймс

1
Приклади, що використовують зворотний виклик prevState з доданими різними методами.
pscl

Що робити, якщо я це роблю так? this.setState({ data: [...this.state.data.slice(0, index), ...this.state.data.slice(index + 1)] }); чи неправильно використовувати this.stateзамість prevStateопції зворотного дзвінка, яку показав @ user1628461?
Тіберіу Максим

Це найкраще рішення, хоча і не найпростіше
Developia

дякую, використовуючи спред, можливо оновити елемент посередині, а не видаляти його, ось що мені потрібно.
Вайбхав Вішал

24

Ось спосіб видалити елемент з масиву в стані, використовуючи синтаксис поширення ES6.

onRemovePerson: (index) => {
  const data = this.state.data;
  this.setState({ 
    data: [...data.slice(0,index), ...data.slice(index+1)]
  });
}

1
Дякую! Це відчувається як найприродніший спосіб зробити це.
fabiomaia

3

Я хочу задзвонити тут, хоча @pscl на це питання вже відповів правильно на якщо хтось інший з тією ж проблемою, що і я. З чотирьох методів, які я даю, я вибрав використовувати синтаксис es6 з функціями стрілки через його стислість та відсутність залежності від зовнішніх бібліотек:

Використання Array.prototype.filter з функціями стрілки ES6

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

Як ви бачите, я вніс невелику модифікацію, щоб ігнорувати тип індексу ( !==до!= ), оскільки в моєму випадку я витягував індекс із рядкового поля.

Ще один корисний момент, якщо ви бачите дивну поведінку під час видалення елемента на стороні клієнта, - НІКОЛИ не використовуйте індекс масиву як ключ для елемента :

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

0

Просто пропозиція, у своєму коді замість того, щоб let newData = prevState.dataви могли використовувати спред, який вводиться в ES6, який ви можете використовуватиlet newData = ...prevState.data для копіювання масиву

Три крапки ... представляє операторів розповсюдження або параметрів відпочинку ,

Це дозволяє розширити вираз масиву або рядок або все, що може бути повторенням, в місцях, де очікується нульовий або більше аргументів для викликів функцій або елементів масиву.

Крім того, ви можете видалити елемент із масиву, виконавши наступне

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

Сподіваюсь, це сприяє !!


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

Сподіваюся, що це допомагає !!!


Хочете пояснити?
Тіберіу Максим

5
Я думаю, це тому, що deleteвилучає елемент, але індекси не оновлюються, і тому це було б недобрим підходом для регулярних потреб.
Кунок
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.