Rimozione dell'elemento dall'array nello stato del componente


131

Sto cercando di trovare il modo migliore per rimuovere un elemento da un array nello stato di un componente. Dal momento che non dovrei modificare this.statedirettamente la variabile, c'è un modo migliore (più conciso) per rimuovere un elemento da un array rispetto a quello che ho qui ?:

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

Grazie.

aggiornato

Questo è stato aggiornato per utilizzare il callback in setState. Questo dovrebbe essere fatto quando si fa riferimento allo stato corrente durante l'aggiornamento.


Dai un'occhiata a ImmutableJS da Facebook che funziona bene con React. link
Jonatan Lundqvist Medén

6
Non vedo nulla di sbagliato nel tuo codice. In effetti è un modo molto idiomatico di farlo.
Dimitar Dimitrov

Risposte:


138

Il modo più pulito per fare ciò che ho visto è con filter:

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

18
@HussienK the _è talvolta usato per rappresentare un argomento inutilizzato. Qui, è l'elemento corrente nella matrice.
chrisM,

1
Sorprendente. Sto usando .filter tutto il tempo ma non ho mai considerato di usarlo in questa situazione. : D
dakt

4
Questo è un codice pulito, tuttavia, per array di grandi dimensioni, questo metodo è lento. Il motivo è che scorre l'intero array per trovare un indice che sembra già essere determinato.
Matt Ellis,

1
@MattEllis Dato che gli aggiornamenti di stato di React sono comunque immutabili, dovrai incorrere in O (n) nella dimensione dell'elenco per copiarlo in ogni caso. Sarei sorpreso se questo fosse un notevole successo di prestazioni.
ephrion

4
La valutazione this.statecome input per this.setState(), anche immutabilmente, non è raccomandata. si prega di consultare la mia risposta sopra per un riferimento ai documenti ufficiali di React su questo argomento.
pscl

87

Puoi usare l' update()aiutante dell'immutabilità direact-addons-update , che fa effettivamente la stessa cosa sotto il cofano, ma quello che stai facendo va bene.

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

Esempio molto più semplice di quello a cui ti sei collegato :)
Koen.

7
react-addons-updateè ora obsoleto (2016). immutability-helperè disponibile in sostituzione. github.com/kolodny/immutability-helper Vedi anche la mia risposta di seguito relativa alla non mutazione this.state direttamente all'interno di this.setState ().
pscl

62

Credo che il riferimento this.stateall'interno di setState()sia scoraggiato ( gli aggiornamenti di stato possono essere asincroni ).

I documenti raccomandano l'uso setState()con una funzione di callback in modo che prevState venga passato in fase di esecuzione quando si verifica l'aggiornamento. Quindi è così che apparirebbe:

Utilizzo di Array.prototype.filter senza ES6

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

Utilizzo di Array.prototype.filter con le funzioni freccia ES6

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

Usando l'immutabilità-aiutante

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

Utilizzando Spread

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

Si noti che in ciascuna istanza, indipendentemente dalla tecnica utilizzata, this.setState()viene passato un callback, non un riferimento all'oggetto al vecchio this.state;


3
Questa è la risposta corretta, vedi anche The Power of Not Mutating Data
Vinnie James,

1
Esempi che utilizzano il callback prevState con varie tecniche aggiunte.
pscl

e se lo facessi così? this.setState({ data: [...this.state.data.slice(0, index), ...this.state.data.slice(index + 1)] }); è sbagliato usare this.stateinvece l' prevStateopzione di callback mostrata da @ user1628461?
Tiberiu Maxim,

Questa è la soluzione migliore, anche se non più semplice
Developia,

grazie, usando spread è possibile aggiornare anche un elemento nel mezzo invece di eliminarlo, ecco cosa mi serviva.
Vaibhav Vishal,

24

Ecco un modo per rimuovere l'elemento dalla matrice nello stato usando la sintassi di diffusione ES6.

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

1
Grazie! Questo sembra il modo più naturale per farlo.
fabiomaia,

3

Voglio entrare qui anche se @pscl ha già risposto correttamente a questa domanda nel caso in cui qualcun altro incontri lo stesso problema che ho fatto. Tra i 4 metodi forniti ho scelto di utilizzare la sintassi es6 con le funzioni freccia a causa della sua concisione e mancanza di dipendenza dalle librerie esterne:

Utilizzo di Array.prototype.filter con le funzioni freccia ES6

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

Come puoi vedere, ho fatto una leggera modifica per ignorare il tipo di indice ( !==a !=) perché nel mio caso stavo recuperando l'indice da un campo stringa.

Un altro punto utile se si riscontra un comportamento strano quando si rimuove un elemento sul lato client è MAI utilizzare l'indice di un array come chiave per l'elemento :

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

Quando React differisce con il DOM virtuale su una modifica, esaminerà i tasti per determinare cosa è cambiato. Quindi se stai usando gli indici e ce n'è uno in meno nell'array, rimuoverà l'ultimo. Usa invece gli ID del contenuto come chiavi, in questo modo.

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

Quanto sopra è un estratto di questa risposta da un post correlato .

Buona programmazione a tutti!


1

È possibile utilizzare questa funzione, se si desidera rimuovere l'elemento (senza indice)

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

1

Come menzionato in un commento alla risposta di ephrion sopra, filter () può essere lento, specialmente con array di grandi dimensioni, in quanto cerca un indice che sembra essere già stato determinato. Questa è una soluzione pulita, ma inefficiente.

In alternativa si può semplicemente "tagliare" l'elemento desiderato e concatenare i frammenti.

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

Spero che questo ti aiuti!


0

È possibile rendere il codice più leggibile con una funzione di supporto di una riga:

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

quindi usalo così:

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

0

Solo un suggerimento, nel tuo codice invece di usarlo let newData = prevState.datapotresti usare spread che è stato introdotto in ES6 che puoi usare let newData = ...prevState.data per copiare l'array

Tre punti ... rappresentano gli operatori sparsi o i parametri di riposo ,

Consente l'espressione di una matrice o stringa o qualsiasi cosa che può essere iterante da espandere in luoghi in cui sono previsti zero o più argomenti per chiamate di funzione o elementi per matrice.

Inoltre è possibile eliminare l'elemento dall'array con il seguente

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

Spero che questo contribuisca !!


-3

Ecco un modo semplice per farlo:

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

Spero che sia d'aiuto!!!


Ti interessa spiegare?
Tiberiu Maxim,

5
Penso che sia perché deleterimuove l'elemento ma gli indici non vengono aggiornati e quindi questo non sarebbe un buon approccio per le esigenze regolari.
Kunok,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.