Suppression d'un élément du tableau dans l'état du composant

131

J'essaie de trouver le meilleur moyen de supprimer un élément d'un tableau dans l'état d'un composant. Puisque je ne devrais pas modifier la this.statevariable directement, y a-t-il un meilleur moyen (plus concis) de supprimer un élément d'un tableau que ce que j'ai ici?:

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

Je vous remercie.

actualisé

Cela a été mis à jour pour utiliser le rappel dans setState. Cela doit être fait lors du référencement de l'état actuel lors de sa mise à jour.

aherriot
la source
Jetez un œil à ImmutableJS de Facebook qui fonctionne bien avec React. lien
Jonatan Lundqvist Medén
6
Je ne vois rien de mal avec votre code. En fait, c'est une façon très idiomatique de le faire.
Dimitar Dimitrov

Réponses:

138

La façon la plus propre de faire cela que j'ai vue est avec filter:

removeItem(index) {
  this.setState({
    data: this.state.data.filter((_, i) => i !== index)
  });
}
éphrion
la source
18
@HussienK the _est parfois utilisé pour représenter un argument inutilisé. Ici, c'est l'élément courant du tableau.
chrisM
1
Incroyable. J'utilise .filter tout le temps mais je n'ai jamais envisagé de l'utiliser dans cette situation. : D
dakt
4
Il s'agit d'un code propre, cependant, pour les grands tableaux, cette méthode est lente. La raison étant, il parcourt le tableau entier pour trouver un index qui semble déjà déterminé.
Matt Ellis le
1
@MattEllis Puisque les mises à jour d'état de React sont de toute façon immuables, vous allez devoir subir O (n) de la taille de la liste pour la copier dans tous les cas. Je serais surpris si c'était un succès significatif en termes de performances.
ephrion
4
L'évaluation this.statecomme entrée this.setState(), même immuablement, n'est pas recommandée. s'il vous plaît voir ma réponse ci-dessus pour une référence aux documents officiels React sur ce sujet.
pscl
87

Vous pouvez utiliser l' update()assistant d'immuabilité dereact-addons-update , qui fait effectivement la même chose sous le capot, mais ce que vous faites est bien.

this.setState(prevState => ({
  data: update(prevState.data, {$splice: [[index, 1]]})
}))
Jonny Buchanan
la source
Exemple beaucoup plus simple que celui auquel vous avez lié :)
Koen.
7
react-addons-updateest désormais obsolète (2016). immutability-helperest disponible en remplacement. github.com/kolodny/immutability-helper Veuillez également voir ma réponse ci-dessous concernant la non-mutation de this.state directement dans this.setState ().
pscl
62

Je crois référencer this.state intérieur de setState()est déconseillé (les mises à jour d'état peuvent être asynchrones ).

La documentation recommande d'utiliser setState()avec une fonction de rappel afin que prevState soit transmis au moment de l'exécution lorsque la mise à jour se produit. Voici donc à quoi cela ressemblerait:

Utilisation de Array.prototype.filter sans ES6

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

Utilisation de Array.prototype.filter avec les fonctions fléchées ES6

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

Utiliser immutability-helper

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

Utiliser Spread

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

Notez que dans chaque instance, quelle que soit la technique utilisée, this.setState()est passé un rappel, pas une référence d'objet à l'ancien this.state;

pscl
la source
3
C'est la bonne réponse, voir aussi The Power of Not Mutating Data
Vinnie James
1
Exemples utilisant le rappel prevState avec l'ajout de diverses techniques.
pscl
et si je le fais comme ça? this.setState({ data: [...this.state.data.slice(0, index), ...this.state.data.slice(index + 1)] }); est-ce mal d'utiliser à la this.stateplace de l' prevStateoption de rappel montrée par @ user1628461?
Tiberiu Maxim
C'est la meilleure solution, mais pas la plus simple
Developia
merci, en utilisant spread, il est également possible de mettre à jour un élément au milieu au lieu de le supprimer, c'est ce dont j'avais besoin.
Vaibhav Vishal
24

Voici un moyen de supprimer l'élément du tableau dans l'état à l'aide de la syntaxe de propagation ES6.

onRemovePerson: (index) => {
  const data = this.state.data;
  this.setState({ 
    data: [...data.slice(0,index), ...data.slice(index+1)]
  });
}
evianpring
la source
1
Merci! Cela semble être la manière la plus naturelle de le faire.
fabiomaia
3

Je souhaite intervenir ici même si cette question a déjà reçu une réponse correcte de @pscl au cas où quelqu'un d'autre rencontrerait le même problème que moi. Sur les 4 méthodes proposées, j'ai choisi d'utiliser la syntaxe es6 avec des fonctions fléchées en raison de sa concision et du manque de dépendance vis-à-vis des bibliothèques externes:

Utilisation de Array.prototype.filter avec les fonctions fléchées ES6

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

Comme vous pouvez le voir, j'ai fait une légère modification pour ignorer le type d'index ( !==à!= ) car dans mon cas, je récupérais l'index à partir d'un champ de chaîne.

Un autre point utile si vous constatez un comportement étrange lors de la suppression d'un élément côté client est de NE JAMAIS utiliser l'index d'un tableau comme clé de l'élément :

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

Lorsque React diffère avec le DOM virtuel lors d'un changement, il examinera les clés pour déterminer ce qui a changé. Donc, si vous utilisez des index et qu'il y en a un de moins dans le tableau, cela supprimera le dernier. Utilisez plutôt les identifiants du contenu comme clés, comme ceci.

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

Ce qui précède est un extrait de cette réponse d'un article connexe .

Bon codage à tous!

c0d3ster
la source
1

Vous pouvez utiliser cette fonction, si vous souhaitez supprimer l'élément (sans index)

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

Comme mentionné dans un commentaire à la réponse d'Ephrion ci-dessus, filter () peut être lent, en particulier avec de grands tableaux, car il effectue une boucle pour rechercher un index qui semble déjà avoir été déterminé. C'est une solution propre mais inefficace.

Comme alternative, on peut simplement «découper» l'élément souhaité et concaténer les fragments.

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

J'espère que cela t'aides!

Matt Ellis
la source
0

Vous pouvez rendre le code plus lisible avec une fonction d'assistance d'une ligne:

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

puis utilisez-le comme ceci:

this.setState(state => ({ places: removeElement(state.places, index) }));
Brian Burns
la source
0

Juste une suggestion, dans votre code au lieu d'utiliser, let newData = prevState.datavous pouvez utiliser spread qui est introduit dans ES6 que vous pouvez utiliserlet newData = ...prevState.data pour copier un tableau

Trois points ... représentent les opérateurs de propagation ou les paramètres de repos ,

Il permet à une expression de tableau, à une chaîne ou à tout ce qui peut être itératif d'être développé à des endroits où zéro ou plusieurs arguments pour les appels de fonction ou des éléments pour le tableau sont attendus.

De plus, vous pouvez supprimer l'élément du tableau avec les éléments suivants

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

J'espère que cela contribue !!

Saddam Kamal
la source
-3

Voici un moyen simple de le faire:

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

J'espère que ça aide!!!

T04435
la source
Soin d'expliquer?
Tiberiu Maxim
5
Je pense que c'est parce que deletesupprime l'élément mais que les index ne sont pas mis à jour et que ce ne serait donc pas une bonne approche pour les besoins réguliers.
Kunok