Usuwanie elementu z tablicy w stanie komponentu

131

Próbuję znaleźć najlepszy sposób na usunięcie elementu z tablicy w stanie komponentu. Ponieważ nie powinienem this.statebezpośrednio modyfikować zmiennej, czy istnieje lepszy sposób (bardziej zwięzły) na usunięcie elementu z tablicy niż to, co mam tutaj ?:

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

Dziękuję Ci.

zaktualizowany

Zostało to zaktualizowane, aby używać wywołania zwrotnego w setState. Należy to zrobić podczas odwoływania się do bieżącego stanu podczas jego aktualizacji.

aherriot
źródło
Spójrz na ImmutableJS z Facebooka, który działa dobrze z React. link
Jonatan Lundqvist Medén
6
Nie widzę nic złego w Twoim kodzie. W rzeczywistości jest to bardzo idiomatyczny sposób.
Dimitar Dimitrov

Odpowiedzi:

142

Najczystszym sposobem na zrobienie tego, jaki widziałem, jest filter:

removeItem(index) {
  this.setState({
    data: this.state.data.filter((_, i) => i !== index)
  });
}
ephrion
źródło
18
@HussienK _jest czasami używany do reprezentowania nieużywanego argumentu. Tutaj jest to bieżący element w tablicy.
chrisM
1
Niesamowity. Cały czas używam .filter, ale nigdy nie rozważałem użycia go w tej sytuacji. : D
dakt
4
To jest czysty kod, jednak w przypadku dużych tablic ta metoda jest powolna. Powodem jest to, że przegląda całą tablicę, aby znaleźć indeks, który wygląda na już określony.
Matt Ellis,
1
@MattEllis Ponieważ aktualizacje stanu Reacta i tak są niezmienne, w każdym przypadku będziesz ponosić O (n) w rozmiarze listy, aby ją skopiować. Byłbym zaskoczony, gdyby był to znaczący hit wydajnościowy.
ephrion
4
Ocenianie this.statejako dane wejściowe this.setState(), nawet niezmienne, nie jest zalecane. zobacz moją odpowiedź powyżej, aby zapoznać się z oficjalnymi dokumentami Reacta na ten temat.
pscl
87

Możesz użyć update()pomocnika niezmiennościreact-addons-update , który skutecznie robi to samo pod maską, ale to, co robisz, jest w porządku.

this.setState(prevState => ({
  data: update(prevState.data, {$splice: [[index, 1]]})
}))
Jonny Buchanan
źródło
Znacznie prostszy przykład niż ten, do którego linkowałeś :)
Koen.
7
react-addons-updatejest teraz przestarzała (2016). immutability-helperjest dostępny jako zamiennik. github.com/kolodny/immutability-helper Proszę również zapoznać się z moją odpowiedzią poniżej dotyczącą nie mutowania this.state bezpośrednio w this.setState ().
pscl
64

Uważam, że odwoływanie się do this.statewewnątrz programu nie setState()jest zalecane ( aktualizacje stanu mogą być asynchroniczne ).

Dokumentacja zaleca używanie setState()z funkcją wywołania zwrotnego, aby prevState był przekazywany w czasie wykonywania, gdy nastąpi aktualizacja. A więc tak by to wyglądało:

Używanie Array.prototype.filter bez ES6

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

Używanie Array.prototype.filter z funkcjami strzałek ES6

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

Korzystanie z pomocnika niezmienności

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

Korzystanie z rozpowszechniania

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

Zauważ, że w każdym przypadku, niezależnie od użytej techniki, this.setState()przekazywane jest wywołanie zwrotne, a nie odwołanie do obiektu this.state;

pscl
źródło
3
To jest poprawna odpowiedź, zobacz także The Power of Not Mutating Data
Vinnie James,
1
Przykłady używające wywołania zwrotnego prevState z dodanymi różnymi technikami.
pscl
co jeśli zrobię to w ten sposób? this.setState({ data: [...this.state.data.slice(0, index), ...this.state.data.slice(index + 1)] }); czy używanie this.statezamiast prevStateopcji callback pokazanej przez @ user1628461 jest niewłaściwe?
Tiberiu Maxim
To najlepsze rozwiązanie, choć nie najprostsze
Developia
dzięki, używając spreadu można również zaktualizować element w środku zamiast go usuwać, tego potrzebowałem.
Vaibhav Vishal
24

Oto sposób na usunięcie elementu z tablicy w stanie przy użyciu składni rozsyłania ES6.

onRemovePerson: (index) => {
  const data = this.state.data;
  this.setState({ 
    data: [...data.slice(0,index), ...data.slice(index+1)]
  });
}
evianpring
źródło
1
Dzięki! To wydaje się najbardziej naturalny sposób na zrobienie tego.
fabiomaia
3

Chcę tu zadzwonić, mimo że @pscl odpowiedział już poprawnie na to pytanie, na wypadek gdyby ktoś inny napotkał ten sam problem, co ja. Z podanych 4 metod wybrałem użycie składni es6 z funkcjami strzałkowymi ze względu na jej zwięzłość i brak zależności od zewnętrznych bibliotek:

Używanie Array.prototype.filter z funkcjami strzałek ES6

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

Jak widać, dokonałem niewielkiej modyfikacji, aby zignorować typ indeksu ( !==to !=), ponieważ w moim przypadku pobierałem indeks z pola tekstowego.

Innym pomocnym punktem, jeśli widzisz dziwne zachowanie podczas usuwania elementu po stronie klienta, jest NIGDY nie używaj indeksu tablicy jako klucza dla elementu :

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

Kiedy React porównuje się z wirtualnym DOM po zmianie, będzie patrzył na klawisze, aby określić, co się zmieniło. Więc jeśli używasz indeksów, a w tablicy jest o jeden mniej, usunie to ostatni. Zamiast tego użyj identyfikatorów treści jako kluczy, w ten sposób.

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

Powyższe jest fragmentem tej odpowiedzi z pokrewnego postu .

Szczęśliwego kodowania wszystkim!

c0d3ster
źródło
1

Możesz użyć tej funkcji, jeśli chcesz usunąć element (bez indeksu)

removeItem(item) {
  this.setState(prevState => {
    data: prevState.data.filter(i => i !== item)
  });
}
julian libor
źródło
1

Jak wspomniano w komentarzu do powyższej odpowiedzi ephriona, filter () może działać wolno, szczególnie w przypadku dużych tablic, ponieważ wykonuje pętlę w celu wyszukania indeksu, który wydaje się być już określony. To czyste, ale nieefektywne rozwiązanie.

Alternatywnie można po prostu „wyciąć” żądany element i połączyć fragmenty.

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

Mam nadzieję że to pomoże!

Matt Ellis
źródło
0

Możesz uczynić kod bardziej czytelnym za pomocą jednowierszowej funkcji pomocniczej:

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

następnie użyj go w ten sposób:

this.setState(state => ({ places: removeElement(state.places, index) }));
Brian Burns
źródło
0

Tylko sugestia, w swoim kodzie zamiast używać let newData = prevState.datamożesz użyć spreadu, który jest wprowadzony w ES6, czyli możesz użyćlet newData = ...prevState.data do kopiowania tablicy

Trzy kropki ... reprezentują operatory rozprzestrzeniania lub parametry odpoczynku ,

Umożliwia rozwinięcie wyrażenia tablicowego lub ciągu znaków lub czegokolwiek, co może być iterowane, w miejscach, w których oczekiwanych jest zero lub więcej argumentów dla wywołań funkcji lub elementów tablicy.

Dodatkowo możesz usunąć element z tablicy za pomocą następujących

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

Mam nadzieję, że to się przyczyni!

Saddam Kamal
źródło
-3

Oto prosty sposób na zrobienie tego:

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

Mam nadzieję, że to pomoże!!!

T04435
źródło
Chcesz wyjaśnić?
Tiberiu Maxim
5
Myślę, że deletedzieje się tak dlatego, że usuwa element, ale indeksy nie są aktualizowane, a zatem nie byłoby to dobre podejście do zwykłych potrzeb.
Kunok