Element aus dem Array im Komponentenstatus entfernen

131

Ich versuche, den besten Weg zu finden, um ein Element aus einem Array im Status einer Komponente zu entfernen. Gibt this.statees eine bessere (prägnantere) Möglichkeit, ein Element aus einem Array zu entfernen, als ich es hier habe, da ich die Variable nicht direkt ändern sollte ?:

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

Danke dir.

Aktualisiert

Dies wurde aktualisiert, um den Rückruf in setState zu verwenden. Dies sollte erfolgen, wenn beim Aktualisieren auf den aktuellen Status verwiesen wird.

aherriot
quelle
Schauen Sie sich ImmutableJS von Facebook an, das gut mit React funktioniert. Link
Jonatan Lundqvist Medén
6
Ich sehe nichts falsch mit Ihrem Code. Tatsächlich ist es eine sehr idiomatische Art, dies zu tun.
Dimitar Dimitrov

Antworten:

138

Der sauberste Weg, dies zu tun, den ich gesehen habe, ist mit filter:

removeItem(index) {
  this.setState({
    data: this.state.data.filter((_, i) => i !== index)
  });
}
Ephrion
quelle
18
@HussienK the _wird manchmal verwendet, um ein nicht verwendetes Argument darzustellen. Hier ist es das aktuelle Element im Array.
ChrisM
1
Tolle. Ich verwende die ganze Zeit .filter, habe aber nie daran gedacht, es in dieser Situation zu verwenden. : D
dakt
4
Dies ist sauberer Code. Bei großen Arrays ist diese Methode jedoch langsam. Grund dafür ist, dass es das gesamte Array durchläuft, um einen Index zu finden, der bereits bestimmt zu sein scheint.
Matt Ellis
1
@MattEllis Da die Statusaktualisierungen von React ohnehin unveränderlich sind, wird in jedem Fall O (n) in der Größe der Liste angezeigt, um sie zu kopieren. Ich wäre überrascht, wenn dies ein bedeutender Leistungseinbruch wäre.
Ephrion
4
Eine Auswertung this.stateals Input für this.setState(), auch unveränderlich, wird nicht empfohlen. In meiner Antwort oben finden Sie einen Verweis auf die offiziellen React-Dokumente zu diesem Thema.
pscl
87

Sie könnten den update()Unveränderlichkeitshelfer von verwendenreact-addons-update , der effektiv dasselbe unter der Haube tut, aber was Sie tun, ist in Ordnung.

this.setState(prevState => ({
  data: update(prevState.data, {$splice: [[index, 1]]})
}))
Jonny Buchanan
quelle
Viel einfacheres Beispiel als das, mit dem Sie verlinkt haben :)
Koen.
7
react-addons-updateist jetzt veraltet (2016). immutability-helperist als Ersatz erhältlich. github.com/kolodny/immutability-helper Siehe auch meine Antwort unten bezüglich der Nicht-Mutation dieses Zustands direkt in this.setState ().
Pscl
62

Ich glaube, dass von Verweisen this.stateinnerhalb von setState()abgeraten wird ( Statusaktualisierungen können asynchron sein ).

In den Dokumenten wird empfohlen, setState()eine Rückruffunktion zu verwenden, damit prevState zur Laufzeit übergeben wird, wenn die Aktualisierung erfolgt. So würde es also aussehen:

Verwenden von Array.prototype.filter ohne ES6

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

Verwenden von Array.prototype.filter mit ES6-Pfeilfunktionen

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

Unveränderlichkeitshilfe verwenden

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

Spread verwenden

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

Beachten Sie, dass in jedem Fall unabhängig von der verwendeten Technik this.setState()ein Rückruf übergeben wird, kein Objektverweis auf den alten this.state;

pscl
quelle
3
Dies ist die richtige Antwort, siehe auch Die Macht, Daten nicht zu mutieren
Vinnie James
1
Beispiele für die Verwendung des prevState-Rückrufs mit verschiedenen hinzugefügten Techniken.
Pscl
Was ist, wenn ich es so mache? Ist this.setState({ data: [...this.state.data.slice(0, index), ...this.state.data.slice(index + 1)] }); es falsch, this.stateanstelle der prevStatevon @ user1628461 angezeigten Rückrufoption zu verwenden?
Tiberiu Maxim
Dies ist die beste Lösung, wenn auch nicht die einfachste
Developia
danke, mit verbreiten ist es möglich, ein element auch in der mitte zu aktualisieren, anstatt es zu löschen, das war es, was ich brauchte.
Vaibhav Vishal
24

Hier ist eine Möglichkeit, das Element im Status mithilfe der ES6-Spread-Syntax aus dem Array zu entfernen.

onRemovePerson: (index) => {
  const data = this.state.data;
  this.setState({ 
    data: [...data.slice(0,index), ...data.slice(index+1)]
  });
}
evianpring
quelle
1
Vielen Dank! Dies scheint der natürlichste Weg zu sein.
Fabiomaia
3

Ich möchte mich hier einschalten, obwohl diese Frage von @pscl bereits richtig beantwortet wurde, falls jemand anderes auf dasselbe Problem stößt wie ich. Von den 4 angegebenen Methoden habe ich mich entschieden, die es6-Syntax mit Pfeilfunktionen zu verwenden, da sie präzise ist und nicht von externen Bibliotheken abhängig ist:

Verwenden von Array.prototype.filter mit ES6-Pfeilfunktionen

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

Wie Sie sehen, habe ich eine geringfügige Änderung vorgenommen, um den Indextyp ( !==to !=) zu ignorieren , da ich in meinem Fall den Index aus einem Zeichenfolgenfeld abgerufen habe.

Ein weiterer hilfreicher Punkt, wenn Sie beim Entfernen eines Elements auf der Clientseite ein seltsames Verhalten feststellen, besteht darin, NIEMALS den Index eines Arrays als Schlüssel für das Element zu verwenden :

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

Wenn sich React bei einer Änderung mit dem virtuellen DOM unterscheidet, werden die Schlüssel überprüft, um festzustellen, was sich geändert hat. Wenn Sie also Indizes verwenden und das Array einen weniger enthält, wird der letzte entfernt. Verwenden Sie stattdessen die IDs des Inhalts wie folgt als Schlüssel.

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

Das Obige ist ein Auszug aus dieser Antwort aus einem verwandten Beitrag .

Viel Spaß beim Codieren!

c0d3ster
quelle
2

Nur ein Vorschlag: In Ihrem Code können Sie anstelle von let newData = prevState.dataSpread den in ES6 eingeführten Spread verwenden, den Sie let newData = ...prevState.data zum Kopieren von Arrays verwenden können

Drei Punkte ... stehen für Spread-Operatoren oder Ruheparameter .

Damit kann ein Array-Ausdruck oder eine Zeichenfolge oder alles, was iteriert werden kann, an Stellen erweitert werden, an denen null oder mehr Argumente für Funktionsaufrufe oder Elemente für ein Array erwartet werden.

Zusätzlich können Sie Elemente aus dem Array wie folgt löschen

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

Hoffe das trägt dazu bei !!

Saddam Kamal
quelle
1

Sie können diese Funktion verwenden, wenn Sie das Element entfernen möchten (ohne Index).

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

Wie in einem Kommentar zur obigen Antwort von ephrion erwähnt, kann filter () langsam sein, insbesondere bei großen Arrays, da nach einem Index gesucht wird, der anscheinend bereits bestimmt wurde. Dies ist eine saubere, aber ineffiziente Lösung.

Alternativ kann man einfach das gewünschte Element herausschneiden und die Fragmente verketten.

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

Hoffe das hilft!

Matt Ellis
quelle
0

Sie können den Code mit einer einzeiligen Hilfsfunktion besser lesbar machen:

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

dann benutze es so:

this.setState(state => ({ places: removeElement(state.places, index) }));
Brian Burns
quelle
-3

Hier ist eine einfache Möglichkeit, dies zu tun:

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

Ich hoffe es hilft!!!

T04435
quelle
Möchtest du das erklären?
Tiberiu Maxim
5
Ich denke, das liegt daran, dass deleteElemente entfernt werden, die Indizes jedoch nicht aktualisiert werden. Daher wäre dies kein guter Ansatz für reguläre Anforderungen.
Kunok