Removendo o elemento da matriz no estado do componente

131

Estou tentando encontrar a melhor maneira de remover um elemento de uma matriz no estado de um componente. Como não devo modificar a this.statevariável diretamente, existe uma maneira melhor (mais concisa) de remover um elemento de uma matriz do que o que tenho aqui?

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

Obrigado.

Atualizada

Isso foi atualizado para usar o retorno de chamada em setState. Isso deve ser feito ao fazer referência ao estado atual durante a atualização.

aherriot
fonte
Dê uma olhada no ImmutableJS do Facebook que funciona bem com o React. link
Jonatan Lundqvist Medén
6
Não vejo nada de errado com o seu código. Na verdade, é uma maneira muito idiomática de fazer isso.
Dimitar Dimitrov

Respostas:

138

A maneira mais limpa de fazer isso que eu já vi é com filter:

removeItem(index) {
  this.setState({
    data: this.state.data.filter((_, i) => i !== index)
  });
}
ephrion
fonte
18
Às _vezes, o @HussienK é usado para representar um argumento não utilizado. Aqui, é o item atual na matriz.
Crisma
1
Surpreendente. Estou usando .filter o tempo todo, mas nunca considerei usá-lo nessa situação. : D
dakt 14/10
4
Este é um código limpo, no entanto, para matrizes grandes, esse método é lento. Por alguma razão, ele percorre toda a matriz para encontrar um índice que parece já estar determinado.
22617 Matt Ellis
1
@MattEllis Como as atualizações de estado do React são imutáveis ​​de qualquer maneira, você estará incorrendo em O (n) no tamanho da lista para copiá-lo em qualquer caso. Eu ficaria surpreso se este fosse um sucesso significativo no desempenho.
ephrion
4
Avaliar this.statecomo entrada this.setState(), mesmo que imutável, não é recomendado. consulte a minha resposta acima para obter uma referência aos documentos oficiais do React sobre este tópico.
Psl
87

Você pode usar o update()auxiliar de imutabilidadereact-addons-update , que efetivamente faz a mesma coisa sob o capô, mas o que você está fazendo está bem.

this.setState(prevState => ({
  data: update(prevState.data, {$splice: [[index, 1]]})
}))
Jonny Buchanan
fonte
Exemplo muito mais simples do que aquele ao qual você se conectou :)
Koen.
7
react-addons-updateagora está obsoleto (2016). immutability-helperestá disponível como um substituto. github.com/kolodny/immutability-helper Além disso, veja a minha resposta abaixo sobre não modificar o this.state diretamente dentro do this.setState ().
pscl 14/02
62

Acredito que a referência this.statedentro de setState()é desencorajada (as atualizações de estado podem ser assíncronas ).

Os documentos recomendam o uso setState() com uma função de retorno de chamada para que prevState seja passado em tempo de execução quando a atualização ocorrer. Então é assim que ficaria:

Usando Array.prototype.filter sem ES6

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

Usando Array.prototype.filter com funções de seta do ES6

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

Usando auxiliar de imutabilidade

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

Observe que em cada instância, independentemente da técnica usada, this.setState()é transmitido um retorno de chamada, não uma referência de objeto ao antigo this.state;

pscl
fonte
3
Esta é a resposta correta, veja também O poder de não modificar dados
Vinnie James
1
Exemplos usando o retorno de chamada prevState com várias técnicas adicionadas.
pscl
e se eu fizer assim? this.setState({ data: [...this.state.data.slice(0, index), ...this.state.data.slice(index + 1)] }); é errado usar em this.statevez da prevStateopção de retorno de chamada mostrada por @ user1628461?
Tiberiu Maxim
Esta é a melhor solução, embora não seja a mais simples #
Developia
obrigado, usando spread é possível atualizar um elemento no meio também, em vez de excluí-lo, é disso que eu precisava.
Vaibhav Vishal
24

Aqui está uma maneira de remover o elemento da matriz no estado usando a sintaxe de expansão ES6.

onRemovePerson: (index) => {
  const data = this.state.data;
  this.setState({ 
    data: [...data.slice(0,index), ...data.slice(index+1)]
  });
}
evianpring
fonte
1
Obrigado! Parece a maneira mais natural de fazê-lo.
Fabiomaia 8/09
3

Quero entrar aqui, mesmo que essa pergunta já tenha sido respondida corretamente por @pscl , caso alguém o mesmo problema que eu. Dos 4 métodos, optei por usar a sintaxe es6 com funções de seta devido à sua concisão e falta de dependência de bibliotecas externas:

Usando Array.prototype.filter com funções de seta do ES6

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

Como você pode ver, fiz uma pequena modificação para ignorar o tipo de índice ( !==para!= ) porque no meu caso eu estava recuperando o índice de um campo de string.

Outro ponto útil se você estiver vendo um comportamento estranho ao remover um elemento no lado do cliente é NUNCA usar o índice de uma matriz como a chave do elemento :

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

Quando o React difere do DOM virtual em uma alteração, ele analisa as chaves para determinar o que mudou. Portanto, se você estiver usando índices e houver um a menos na matriz, ele removerá o último. Em vez disso, use os IDs do conteúdo como chaves, assim.

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

O acima é um trecho de desta resposta de um post relacionado .

Feliz codificação para todos!

c0d3ster
fonte
1

Você pode usar esta função, se desejar remover o elemento (sem índice)

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

Como mencionado em um comentário à resposta de ephrion acima, filter () pode ser lento, especialmente com matrizes grandes, pois ele procura um índice que parece já ter sido determinado. Esta é uma solução limpa, mas ineficiente.

Como alternativa, pode-se simplesmente "cortar" o elemento desejado e concatenar os fragmentos.

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

Espero que isto ajude!

Matt Ellis
fonte
0

Você pode tornar o código mais legível com uma função auxiliar de uma linha:

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

então use-o assim:

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

Apenas uma sugestão: no seu código, em vez de usar, let newData = prevState.datavocê pode usar o spread, que é introduzido no ES6, ou seja, você pode usarlet newData = ...prevState.data para copiar a matriz

Três pontos ... representam operadores de expansão ou parâmetros de repouso ,

Ele permite que uma expressão ou string de matriz ou qualquer coisa que possa ser iterativa seja expandida em locais onde são esperados zero ou mais argumentos para chamadas de função ou elementos para matriz.

Além disso, você pode excluir o item da matriz com os seguintes

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

Espero que isso contribua !!

Saddam Kamal
fonte
-3

Aqui está uma maneira simples de fazer isso:

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 que ajude!!!

T04435
fonte
Gostaria de explicar?
Tiberiu Maxim
5
Eu acho que é porque deleteremove o item, mas os índices não são atualizados e, portanto, isso não seria uma boa abordagem para necessidades regulares.
Kunok