Eliminar elemento de la matriz en estado componente

131

Estoy tratando de encontrar la mejor manera de eliminar un elemento de una matriz en el estado de un componente. Como no debería modificar la this.statevariable directamente, ¿hay una forma mejor (más concisa) de eliminar un elemento de una matriz que la que tengo aquí ?:

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

Gracias.

actualizado

Esto se ha actualizado para usar la devolución de llamada en setState. Esto debe hacerse al hacer referencia al estado actual mientras se actualiza.

aherriot
fuente
Echa un vistazo a ImmutableJS de Facebook que funciona bien con React. enlace
Jonatan Lundqvist Medén
66
No veo nada malo con tu código. De hecho, es una forma muy idiomática de hacerlo.
Dimitar Dimitrov

Respuestas:

138

La forma más limpia de hacer esto que he visto es con filter:

removeItem(index) {
  this.setState({
    data: this.state.data.filter((_, i) => i !== index)
  });
}
ephrion
fuente
18
@HussienK the a _veces se usa para representar un argumento no utilizado. Aquí, es el elemento actual en la matriz.
chrisM
1
Asombroso. Estoy usando .filter todo el tiempo, pero nunca consideré usarlo en esta situación. : D
dakt
44
Este es un código limpio, sin embargo, para matrices grandes, este método es lento. La razón es que recorre toda la matriz para encontrar un índice que parece estar ya determinado.
Matt Ellis
1
@MattEllis Como las actualizaciones de estado de React son inmutables de todos modos, incurrirá en O (n) en el tamaño de la lista para copiarlo en cualquier caso. Me sorprendería si este fuera un éxito significativo en el rendimiento.
Ephrion
44
No se recomienda evaluar this.statecomo entrada this.setState(), incluso de forma inmutable. consulte mi respuesta anterior para obtener una referencia a los documentos oficiales de React sobre este tema.
pscl
87

Podría usar el update()ayudante de inmutabilidadreact-addons-update , que efectivamente hace lo mismo debajo del capó, pero lo que está haciendo está bien.

this.setState(prevState => ({
  data: update(prevState.data, {$splice: [[index, 1]]})
}))
Jonny Buchanan
fuente
Un ejemplo mucho más simple que el que vinculaste :)
Koen.
77
react-addons-updateahora está en desuso (2016). immutability-helperestá disponible como reemplazo. github.com/kolodny/immutability-helper También vea mi respuesta a continuación sobre no mutar this.state directamente dentro de this.setState ().
pscl
62

Creo que no se recomienda hacer referencia al this.stateinterior setState()( las actualizaciones de estado pueden ser asincrónicas ).

Los documentos recomiendan usar setState()con una función de devolución de llamada para que se pase prevState en tiempo de ejecución cuando se produce la actualización. Así es como se vería:

Usando Array.prototype.filter sin ES6

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

Uso de Array.prototype.filter con funciones de flecha ES6

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

Usando inmutability-helper

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

Usando Spread

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

Tenga en cuenta que en cada caso, independientemente de la técnica utilizada, this.setState()se pasa una devolución de llamada, no una referencia de objeto a la anterior this.state;

pscl
fuente
3
Esta es la respuesta correcta, ver también El poder de no mutar datos
Vinnie James
1
Ejemplos que utilizan la devolución de llamada prevState con varias técnicas agregadas.
pscl
¿Qué pasa si lo hago así? this.setState({ data: [...this.state.data.slice(0, index), ...this.state.data.slice(index + 1)] }); ¿Es incorrecto usar en this.statelugar de la prevStateopción de devolución de llamada mostrada por @ user1628461?
Tiberiu Maxim
Esta es la mejor solución, aunque no la más simple
Developia
gracias, usando spread es posible actualizar un elemento en el medio también en lugar de eliminarlo, eso es lo que necesitaba.
Vaibhav Vishal
24

Aquí hay una manera de eliminar el elemento de la matriz en el estado utilizando la sintaxis de propagación ES6.

onRemovePerson: (index) => {
  const data = this.state.data;
  this.setState({ 
    data: [...data.slice(0,index), ...data.slice(index+1)]
  });
}
evianpring
fuente
1
¡Gracias! Esto se siente como la forma más natural de hacerlo.
fabiomaia
3

Quiero intervenir aquí aunque @pscl ya haya respondido correctamente a esta pregunta en caso de que alguien más se encuentre con el mismo problema que yo. De los 4 métodos proporcionados, elegí usar la sintaxis es6 con funciones de flecha debido a su concisión y falta de dependencia de bibliotecas externas:

Uso de Array.prototype.filter con funciones de flecha ES6

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

Como puede ver, hice una ligera modificación para ignorar el tipo de índice ( !==a !=) porque en mi caso estaba recuperando el índice de un campo de cadena.

Otro punto útil si está viendo un comportamiento extraño al eliminar un elemento en el lado del cliente es NUNCA usar el índice de una matriz como la clave para el elemento :

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

Cuando React difiere con el DOM virtual en un cambio, observará las claves para determinar qué ha cambiado. Entonces, si está utilizando índices y hay uno menos en la matriz, eliminará el último. En su lugar, use las identificaciones del contenido como claves, como esta.

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

Lo anterior es un extracto de esta respuesta de una publicación relacionada .

¡Feliz codificación para todos!

c0d3ster
fuente
1

Puede usar esta función, si desea eliminar el elemento (sin índice)

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

Como se mencionó en un comentario a la respuesta de ephrion anterior, filter () puede ser lento, especialmente con matrices grandes, ya que realiza un bucle para buscar un índice que parece haber sido determinado ya. Esta es una solución limpia pero ineficiente.

Como alternativa, uno simplemente puede 'cortar' el elemento deseado y concatenar los fragmentos.

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

¡Espero que esto ayude!

Matt Ellis
fuente
0

Puede hacer que el código sea más legible con una función auxiliar de una línea:

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

luego úsalo así:

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

Solo una sugerencia, en su código, en lugar de usar let newData = prevState.data, podría usar propagación, que se introduce en ES6, que puede usarlet newData = ...prevState.data para copiar la matriz

Tres puntos ... representa operadores de dispersión o parámetros de descanso ,

Permite que una expresión de matriz o cadena o cualquier cosa que pueda iterarse se expanda en lugares donde se esperan cero o más argumentos para llamadas a funciones o elementos para matriz.

Además, puede eliminar el elemento de la matriz con los siguientes

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

Espero que esto contribuya !!

saddam kamal
fuente
-3

Aquí hay una manera simple de hacerlo:

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

¡¡¡Espero eso ayude!!!

T04435
fuente
¿Le importaria explicar?
Tiberiu Maxim
55
Creo que es porque deleteelimina el elemento pero los índices no se actualizan y, por lo tanto, este no sería un buen enfoque para las necesidades regulares.
Kunok