Reagir: como atualizar state.item [1] no estado usando setState?


259

Estou criando um aplicativo em que o usuário pode criar seu próprio formulário. Por exemplo, especifique o nome do campo e os detalhes de quais outras colunas devem ser incluídas.

O componente está disponível como um JSFiddle aqui .

Meu estado inicial é assim:

var DynamicForm = React.createClass({
  getInitialState: function() {
   var items = {};
   items[1] = { name: 'field 1', populate_at: 'web_start',
                same_as: 'customer_name',
                autocomplete_from: 'customer_name', title: '' };
   items[2] = { name: 'field 2', populate_at: 'web_end',
                same_as: 'user_name', 
                    autocomplete_from: 'user_name', title: '' };

     return { items };
   },

  render: function() {
     var _this = this;
     return (
       <div>
         { Object.keys(this.state.items).map(function (key) {
           var item = _this.state.items[key];
           return (
             <div>
               <PopulateAtCheckboxes this={this}
                 checked={item.populate_at} id={key} 
                   populate_at={data.populate_at} />
            </div>
            );
        }, this)}
        <button onClick={this.newFieldEntry}>Create a new field</button>
        <button onClick={this.saveAndContinue}>Save and Continue</button>
      </div>
    );
  }

Desejo atualizar o estado quando o usuário altera qualquer um dos valores, mas estou com dificuldades para segmentar o objeto correto:

var PopulateAtCheckboxes = React.createClass({
  handleChange: function (e) {
     item = this.state.items[1];
     item.name = 'newName';
     items[1] = item;
     this.setState({items: items});
  },
  render: function() {
    var populateAtCheckbox = this.props.populate_at.map(function(value) {
      return (
        <label for={value}>
          <input type="radio" name={'populate_at'+this.props.id} value={value}
            onChange={this.handleChange} checked={this.props.checked == value}
            ref="populate-at"/>
          {value}
        </label>
      );
    }, this);
    return (
      <div className="populate-at-checkboxes">
        {populateAtCheckbox}
      </div>
    );
  }
});

Como devo criar this.setStatepara atualizá-lo items[1].name?



1
Qual é a sua resposta selecionada para esta pergunta?
Braian Mellor

Respostas:


126

Você pode usar o updateauxiliar de imutabilidade para isso :

this.setState({
  items: update(this.state.items, {1: {name: {$set: 'updated field name'}}})
})

Ou se você não se importa em poder detectar alterações neste item em um shouldComponentUpdate()método de ciclo de vida usando ===, você pode editar o estado diretamente e forçar o componente a renderizar novamente - isso é efetivamente o mesmo que a resposta do @limelights, pois é retirando um objeto do estado e editando-o.

this.state.items[1].name = 'updated field name'
this.forceUpdate()

Adição pós-edição:

Confira a lição Comunicação de componentes simples do treinamento de reação para obter um exemplo de como passar uma função de retorno de chamada de um pai que mantém um estado para um componente filho que precisa acionar uma alteração de estado.


125

Como há muita desinformação nesse segmento, veja como você pode fazer isso sem as bibliotecas auxiliares:

handleChange: function (e) {
    // 1. Make a shallow copy of the items
    let items = [...this.state.items];
    // 2. Make a shallow copy of the item you want to mutate
    let item = {...items[1]};
    // 3. Replace the property you're intested in
    item.name = 'newName';
    // 4. Put it back into our array. N.B. we *are* mutating the array here, but that's why we made a copy first
    items[1] = item;
    // 5. Set the state to our new copy
    this.setState({items});
},

Você pode combinar as etapas 2 e 3 se desejar:

let item = {
    ...items[1],
    name: 'newName'
}

Ou você pode fazer a coisa toda em uma linha:

this.setState(({items}) => ({
    items: [
        ...items.slice(0,1),
        {
            ...items[1],
            name: 'newName',
        },
        ...items.slice(2)
    ]
}));

Nota: eu fiz items uma matriz. OP usou um objeto. No entanto, os conceitos são os mesmos.


Você pode ver o que está acontecendo no seu terminal / console:

 node
> items = [{name:'foo'},{name:'bar'},{name:'baz'}]
[ { name: 'foo' }, { name: 'bar' }, { name: 'baz' } ]
> clone = [...items]
[ { name: 'foo' }, { name: 'bar' }, { name: 'baz' } ]
> item1 = {...clone[1]}
{ name: 'bar' }
> item1.name = 'bacon'
'bacon'
> clone[1] = item1
{ name: 'bacon' }
> clone
[ { name: 'foo' }, { name: 'bacon' }, { name: 'baz' } ]
> items
[ { name: 'foo' }, { name: 'bar' }, { name: 'baz' } ] // good! we didn't mutate `items`
> items === clone
false // these are different objects
> items[0] === clone[0]
true // we don't need to clone items 0 and 2 because we're not mutating them (efficiency gains!)
> items[1] === clone[1]
false // this guy we copied

4
@TranslucentCloud Oh yeah, os métodos auxiliares são definitivamente bom, mas eu acho que todo mundo deve saber o que está acontecendo sob o capô :-)
MPEN

Por que precisamos das etapas 2 e 3? Por que não podemos mudar o clone diretamente após o primeiro passo?
Evmorov 5/12

1
@ Evmorov Porque não é um clone profundo. O passo 1 é apenas a clonagem da matriz, não os objetos dentro. Em outras palavras, cada um dos objetos dentro da nova matriz ainda "aponta para" os objetos existentes na memória - a mutação de um mutará o outro (eles são o mesmo). Veja também a parte items[0] === clone[0]no meu exemplo de terminal na parte inferior. Triplo =verifica se os objetos se referem à mesma coisa.
MPEN

Torna-se mais complicado se você não souber o índice do item na matriz.
Ian Warburton

@IanWarburton Na verdade não. items.findIndex()deve fazer um trabalho curto disso.
MPEN

92

Caminho errado!

handleChange = (e) => {
    const { items } = this.state;
    items[1].name = e.target.value;

    // update state
    this.setState({
        items,
    });
};

Como apontado por muitos desenvolvedores melhores nos comentários: mudar o estado está errado!

Levei um tempo para descobrir isso. Acima funciona, mas tira o poder do React. Por exemplo, componentDidUpdateisso não será visto como uma atualização porque foi modificado diretamente.

Portanto, o caminho certo seria:

handleChange = (e) => {
    this.setState(prevState => ({
        items: {
            ...prevState.items,
            [prevState.items[1].name]: e.target.value,
        },
    }));
};

25
ES6 só porque você está usando "const"?
Nkollaw

49
Não está chamando items[1].role = e.target.valueestado mutante diretamente?
antony

28
você está mudando o estado, isso é totalmente contra a idéia de manter o estado imutável como reagir sugerido. Isso pode causar muita dor em um grande aplicativo.
Ncubica

8
@MarvinVK, sua resposta diz "Portanto, a melhor prática seria:" seguida pelo uso de "this.forceUpdate ();" que não é recomendado, pois pode ser substituído por setState (), consulte facebook.github.io/react/docs/react-component.html#state . É melhor mudar isso para não confundir os futuros leitores.
James Z.

8
Só queria salientar que muitos dos comentários são irrelevantes agora devido a edições e o caminho certo é realmente o caminho certo.
heez

50

Para modificar objetos / variáveis ​​profundamente aninhados no estado do React, normalmente são utilizados três métodos: JavaScript vanilla Object.assign, auxiliar da imutabilidade e cloneDeepdo Lodash .

Também existem muitas outras bibliotecas de terceiros menos populares para conseguir isso, mas nesta resposta, abordarei apenas essas três opções. Além disso, existem alguns métodos JavaScript de baunilha adicionais, como a dispersão de array (consulte a resposta do @ mpen, por exemplo), mas eles não são muito intuitivos, fáceis de usar e capazes de lidar com todas as situações de manipulação de estado.

Como foi apontado inúmeras vezes nos principais comentários votados para as respostas, cujos autores propõem uma mutação direta de estado: simplesmente não faça isso . Esse é um antipadrão padrão do React, que inevitavelmente levará a consequências indesejadas. Aprenda da maneira certa.

Vamos comparar três métodos amplamente usados.

Dada essa estrutura de objeto de estado:

state = {
    outer: {
        inner: 'initial value'
    }
}

Você pode usar os seguintes métodos para atualizar o mais interno inner valor do campo sem afetar o restante do estado.

1. Object.assign do JavaScript Vanilla

const App = () => {
  const [outer, setOuter] = React.useState({ inner: 'initial value' })

  React.useEffect(() => {
    console.log('Before the shallow copying:', outer.inner) // initial value
    const newOuter = Object.assign({}, outer, { inner: 'updated value' })
    console.log('After the shallow copy is taken, the value in the state is still:', outer.inner) // initial value
    setOuter(newOuter)
  }, [])

  console.log('In render:', outer.inner)

  return (
    <section>Inner property: <i>{outer.inner}</i></section>
  )
}

ReactDOM.render(
  <App />,
  document.getElementById('react')
)
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.10.2/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.10.2/umd/react-dom.production.min.js"></script>

<main id="react"></main>

Lembre-se de que o Object.assign não executará uma clonagem profunda , pois apenas copia os valores das propriedades , e é por isso que o que faz é chamado de cópia superficial (consulte os comentários).

Para que isso funcione, devemos manipular apenas as propriedades dos tipos primitivos ( outer.inner), ou seja, cadeias, números, booleanos.

Neste exemplo, estamos criando uma nova constante ( const newOuter...), using Object.assign, que cria um objeto vazio ( {}), copia o outerobjeto ( { inner: 'initial value' }) e, em seguida, copia um objeto diferente { inner: 'updated value' } sobre ele.

Dessa forma, no final, a newOuterconstante recém-criada manterá um valor { inner: 'updated value' }desde que a innerpropriedade foi substituída. Este newOuteré um objeto totalmente novo, que não está vinculado ao objeto no estado, portanto pode ser alterado conforme necessário e o estado permanecerá o mesmo e não será alterado até que o comando para atualizá-lo seja executado.

A última parte é usar o setOuter()setter para substituir o original outerno estado por um newOuterobjeto recém-criado (apenas o valor será alterado, o nome da propriedade outernão será).

Agora imagine que temos um estado mais profundo state = { outer: { inner: { innerMost: 'initial value' } } }. Poderíamos tentar criar o newOuterobjeto e preenchê-lo com o outerconteúdo do estado, mas Object.assignnão conseguiremos copiar innerMosto valor desta para o newOuterobjeto recém-criado, poisinnerMost está aninhado muito profundamente.

Você ainda pode copiar inner, como no exemplo acima, mas como agora é um objeto e não um primitivo, a referência de newOuter.innerserá copiada para o outer.innerlugar, o que significa que acabaremos com um newOuterobjeto local diretamente vinculado ao objeto no estado .

Isso significa que, neste caso, mutações do localmente criado newOuter.innerafetarão diretamente o outer.innerobjeto (no estado), uma vez que na verdade se tornaram a mesma coisa (na memória do computador).

Object.assign portanto, só funcionará se você tiver uma estrutura de estado profundo relativamente simples de um nível, com membros mais internos mantendo valores do tipo primitivo.

Se você tiver objetos mais profundos (2º nível ou mais), que você deve atualizar, não use Object.assign. Você corre o risco de mudar o estado diretamente.

2. O clone de Lodash

const App = () => {
  const [outer, setOuter] = React.useState({ inner: 'initial value' })

  React.useEffect(() => {
    console.log('Before the deep cloning:', outer.inner) // initial value
    const newOuter = _.cloneDeep(outer) // cloneDeep() is coming from the Lodash lib
    newOuter.inner = 'updated value'
    console.log('After the deeply cloned object is modified, the value in the state is still:', outer.inner) // initial value
    setOuter(newOuter)
  }, [])

  console.log('In render:', outer.inner)

  return (
    <section>Inner property: <i>{outer.inner}</i></section>
  )
}

ReactDOM.render(
  <App />,
  document.getElementById('react')
)
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.10.2/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.10.2/umd/react-dom.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.15/lodash.min.js"></script>

<main id="react"></main>

O cloneDeep da Lodash é muito mais simples de usar. Ele executa uma clonagem profunda , por isso é uma opção robusta, se você tiver um estado bastante complexo com objetos ou matrizes de vários níveis. Apenas cloneDeep()a propriedade do estado de nível superior, modifique a parte clonada da maneira que desejar e setOuter()retorne ao estado.

3. auxiliar da imutabilidade

const App = () => {
  const [outer, setOuter] = React.useState({ inner: 'initial value' })
  
  React.useEffect(() => {
    const update = immutabilityHelper
    console.log('Before the deep cloning and updating:', outer.inner) // initial value
    const newOuter = update(outer, { inner: { $set: 'updated value' } })
    console.log('After the cloning and updating, the value in the state is still:', outer.inner) // initial value
    setOuter(newOuter)
  }, [])

  console.log('In render:', outer.inner)

  return (
    <section>Inner property: <i>{outer.inner}</i></section>
  )
}

ReactDOM.render(
  <App />,
  document.getElementById('react')
)
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.10.2/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.10.2/umd/react-dom.production.min.js"></script>
<script src="https://wzrd.in/standalone/immutability-helper@3.0.0"></script>

<main id="react"></main>

immutability-helperleva-lo para um nível totalmente novo, ea coisa legal sobre isso é que ele pode não só $setvaloriza a itens do estado, mas também $push, $splice, $merge(etc.)-los. Aqui está uma lista de comandos disponíveis.

Notas laterais

Novamente, lembre-se de que setOuter apenas modifica as propriedades de primeiro nível do objeto de estado ( outernesses exemplos), não as profundamente aninhadas ( outer.inner). Se ele se comportasse de maneira diferente, essa pergunta não existiria.

Qual é o certo para o seu projeto?

Se você não quiser ou não puder usar dependências externas e tiver uma estrutura de estado simples , atenha-se a Object.assign.

Se vocês manipular um estado enorme e / ou complexo , o Lodash cloneDeepé uma escolha sábia.

Se você precisar recursos avançados , ou seja, se sua estrutura de estado for complexa e você precisar executar todos os tipos de operações, tente immutability-helper, é uma ferramenta muito avançada que pode ser usada para manipulação de estado.

...ou você realmente precisa fazer isso?

Se você mantiver dados complexos no estado do React, talvez seja um bom momento para pensar em outras maneiras de lidar com eles. A definição correta de objetos de estado complexos nos componentes React não é uma operação simples, e eu sugiro fortemente pensar em abordagens diferentes.

Provavelmente, é melhor você manter seus dados complexos em uma loja Redux, configurando-os lá usando redutores e / ou sagas e acessando-os usando seletores.


Object.assignnão executa uma cópia profunda. Digamos, se a = {c: {d: 1}}e b = Object.assign({}, a), então você executa b.c.d = 4, então a.c.destá mutado.
Awol

Você está certo, o valor 1do objeto mais interno ( a.c.d) será alterado. Mas se você transferir novamente o sucessor de primeiro nível de b, assim:: b.c = {f: 1}a parte correspondente anão será modificada (ela permanecerá {d: 1}). Boa captura de qualquer maneira, vou atualizar a resposta imediatamente.
Neurotransmitter

O que você definiu é realmente um shallow copye não um deep copy. É fácil confundir o que shallow copysignifica. Em shallow copy, a !== bmas para cada chave do objeto de origem a,a[key] === b[key]
Awol

Sim, explicitamente mencionado superficialidade Object.assignna resposta.
Neurotransmitter

JSON.parse(JSON.stringify(object))também é uma variante do clone profundo. O desempenho é pior do que o lodash cloneDeep. measurethat.net/Benchmarks/Show/2751/0/...
tylik

35

Eu tive o mesmo problema. Aqui está uma solução simples que funciona!

const newItems = [...this.state.items];
newItems[item] = value;
this.setState({ items:newItems });

11
@TranslucentCloud - certamente não é uma mutação direta. a matriz original foi clonada, modificada e o estado foi definido novamente usando a matriz clonada.
Vsync

@ vsync sim, agora depois de editar a resposta original, isso não é uma mutação.
Neurotransmitter

2
@TranslucentCloud - Antes, minha edição não tem nada a ver com isso, muito obrigado pela atitude. @ Jonas aqui apenas cometeu um erro simples em sua resposta, usando {chaves em vez dos suportes que eu tinha sanado
vsync

31

De acordo com a documentação do React no setState , o uso Object.assignsugerido por outras respostas aqui não é o ideal. Devido à natureza do setStatecomportamento assíncrono de uma empresa, as chamadas subseqüentes que usam essa técnica podem substituir as anteriores, causando resultados indesejáveis.

Em vez disso, os documentos do React recomendam usar o formato atualizador setStateque opera no estado anterior. Lembre-se de que, ao atualizar uma matriz ou objeto, você deve retornar uma nova matriz ou objeto, pois o React exige que preservemos a imutabilidade do estado. Usando o operador de dispersão da sintaxe ES6 para copiar superficialmente uma matriz, a criação ou atualização de uma propriedade de um objeto em um determinado índice da matriz seria assim:

this.setState(prevState => {
    const newItems = [...prevState.items];
    newItems[index].name = newName;
    return {items: newItems};
})

2
Esta é a resposta apropriada se você usa o ES6. É inline o que @Jonas respondeu. No entanto, devido à explicação, destaca-se.
21819 Sourabh

Sim, este código está funcionando perfeitamente bem e muito simples ..
Shoeb Mirza

29

Primeiro, pegue o item que deseja, altere o que deseja nesse objeto e coloque-o novamente no estado. A maneira como você está usando o estado passando apenas um objeto getInitialStateseria muito mais fácil se você usasse um objeto com chave.

handleChange: function (e) {
   item = this.state.items[1];
   item.name = 'newName';
   items[1] = item;

   this.setState({items: items});
}

3
Nah, produz Uncaught TypeError: Cannot read property 'items' of null.
martins

Não, não vai. Seu erro provavelmente decorre da maneira como você está fazendo getInitialState.
Henrik Andersson

10
@HenrikAndersson Algo não parece certo no seu exemplo. itemsnão está definido em nenhum lugar.
Edward D'Souza

1
@ EdwardD'Souza Você está absolutamente correto! Minha resposta é mostrar como deve ser definido e usado. A maneira como o código do solicitante é configurado não funciona para o que ele / ela gostaria, e é por isso que a necessidade de um objeto com chave é necessária.
Henrik Andersson

7
Este é um antipadrão típico do React; você está atribuindo itemuma referência à própria this.state.items[1]variável do estado . Então você modifica item( item.name = 'newName') e, portanto, modifica o estado diretamente, o que é altamente desencorajado. No seu exemplo, nem é necessário chamar this.setState({items: items}), porque o estado já está mutado diretamente.
Neurotransmissor

22

Não mude o estado no lugar. Isso pode causar resultados inesperados. Eu aprendi minha lição! Sempre trabalhe com uma cópia / clone, Object.assign()é bom:

item = Object.assign({}, this.state.items[1], {name: 'newName'});
items[1] = item;
this.setState({items: items});

https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Object/assign


8
o que está itemsno seu exemplo? Você quis dizer com this.state.itemsalgo mais?
Buh Buh

Eu testei isso, mas ele está faltando uma linha. Acima items[1] = item;deve haver uma linha dizendo items = this.state.items;. Cuidado com o meu javascript está enferrujado e estou aprendendo reagir durante meu projeto de casa, então eu não tenho idéia se isso é bom ou ruim :-)
Greg0ry

4

É realmente simples.

Primeiro, puxe o objeto de itens inteiro do estado, atualize a parte do objeto de itens conforme desejado e coloque o objeto de itens inteiro novamente no estado via setState.

handleChange: function (e) {
  items = Object.assign(this.state.items); // Pull the entire items object out. Using object.assign is a good idea for objects.
  items[1].name = 'newName'; // update the items object as needed
  this.setState({ items }); // Put back in state
}

"Object.assign funcionará se você tiver uma estrutura de estado profundo relativamente simples de um nível, com membros mais internos mantendo valores do tipo primitivo".
Neurotransmissor

3

Como nenhuma das opções acima era ideal para mim, acabei usando o mapa:

this.setState({items: this.state.items.map((item,idx)=> idx!==1 ?item :{...item,name:'new_name'}) })

2

Sem mutação:

// given a state
state = {items: [{name: 'Fred', value: 1}, {name: 'Wilma', value: 2}]}

// This will work without mutation as it clones the modified item in the map:
this.state.items
   .map(item => item.name === 'Fred' ? {...item, ...{value: 3}} : item)

this.setState(newItems)

2
Não consigo ver onde newItemsestá definido.
Neurotransmissor

Um mapa mais uma comparação na matriz não é horrível para o desempenho?
Natassia Tavares

@NatassiaTavares .. what? talvez você esteja confuso fo ofou o forEachmapa seja o mais rápido.
Deano

1

Achei isso surpreendentemente difícil e nenhuma mágica espalhada pelo ES6 parecia funcionar como esperado. Estava usando uma estrutura como essa para obter propriedades do elemento renderizado para fins de layout.

encontrado usando o updatemétodo de immutability-helperpara ser o mais direto neste exemplo simplificado:

constructor(props) {
    super(props)
    this.state = { values: [] }
    this.updateContainerState = this.updateContainerState.bind(this)
  }

updateContainerState(index, value) {
    this.setState((state) => update(state, { values: { [index]: { $set: value } } }))
  }

conforme adaptado de https://github.com/kolodny/immutability-helper#computed-property-names

do membro da matriz a ser atualizado é um objeto complexo mais aninhado, use o método apropriado de cópia em profundidade base na complexidade.

Certamente, existem maneiras melhores de lidar com parâmetros de layout, mas é sobre como lidar com matrizes. Os valores relevantes para cada elemento filho também podem ser calculados fora deles, mas achei mais conveniente passar o containerState para baixo, para que os filhos possam buscar propriedades à vontade e atualizar a matriz do estado pai em seu índice especificado.

import React from 'react'
import update from 'immutability-helper'
import { ContainerElement } from './container.component.style.js'
import ChildComponent from './child-component'
export default class ContainerComponent extends React.Component {
  constructor(props) {
    super(props)
    this.state = { values: [] }
    this.updateContainerState = this.updateContainerState.bind(this)
  }

  updateContainerState(index, value) {
    this.setState((state) => update(state, { values: { [index]: { $set: value } } }))
  }

  // ...

  render() {
    let index = 0
    return (
      <ContainerElement>
      <ChildComponent
        index={index++}
        containerState={this.state}
        updateContainerState={this.updateContainerState}
      />
      <ChildComponent
        index={index++}
        containerState={this.state}
        updateContainerState={this.updateContainerState}
      />
      </ContainerElement>
    )
  }
}

1

Use mapa de matriz com função de seta, em uma linha

this.setState({
    items: this.state.items.map((item, index) =>
      index === 1 ? { ...item, name: 'newName' } : item,
   )
})

1
Isso é basicamente idêntico a esta outra resposta postada em torno de um ano atrás
CertainPerformance


0

Gostaria de mover a mudança de identificador de função e adicionar um parâmetro de índice

handleChange: function (index) {
    var items = this.state.items;
    items[index].name = 'newName';
    this.setState({items: items});
},

para o componente de formulário Dinâmico e passe-o para o componente PopulateAtCheckboxes como um suporte. À medida que você percorre seus itens, você pode incluir um contador adicional (chamado índice no código abaixo) a ser passado para a mudança de identificador, como mostrado abaixo

{ Object.keys(this.state.items).map(function (key, index) {
var item = _this.state.items[key];
var boundHandleChange = _this.handleChange.bind(_this, index);
  return (
    <div>
        <PopulateAtCheckboxes this={this}
            checked={item.populate_at} id={key} 
            handleChange={boundHandleChange}
            populate_at={data.populate_at} />
    </div>
);
}, this)}

Por fim, você pode ligar para o ouvinte de alterações, como mostrado abaixo aqui

<input type="radio" name={'populate_at'+this.props.id} value={value} onChange={this.props.handleChange} checked={this.props.checked == value} ref="populate-at"/>

Nunca mude o estado do React diretamente.
Neurotransmissor

0

Se você precisar alterar apenas parte do Array, Você tem um componente de reação com o estado definido como.

state = {items: [{name: 'red-one', value: 100}, {name: 'green-one', value: 999}]}

É melhor atualizar o red-oneda Arrayseguinte maneira:

const itemIndex = this.state.items.findIndex(i=> i.name === 'red-one');
const newItems = [
   this.state.items.slice(0, itemIndex),
   {name: 'red-one', value: 666},
   this.state.items.slice(itemIndex)
]

this.setState(newItems)

o que é newArray? você quer dizer newItems? Se sim, isso não deixaria o estado com apenas um item depois?
micnil

Isso apresentará uma nova propriedade newItemsao stateobjeto e não atualizará a itemspropriedade existente .
Neurotransmissor

0

ou se você possui uma lista gerada dinamicamente e não conhece o índice, mas apenas possui a chave ou o ID:

let ItemsCopy = []
let x = this.state.Items.map((entry) =>{

    if(entry.id == 'theIDYoureLookingFor')
    {
        entry.PropertyToChange = 'NewProperty'
    }

    ItemsCopy.push(entry)
})


this.setState({Items:ItemsCopy});

0

Tente com o código:

this.state.items[1] = 'new value';
var cloneObj = Object.assign({}, this.state.items);

this.setState({items: cloneObj });

0

O seguinte código foi fácil para o meu cérebro sem graça. Removendo o Objeto e Substituindo pelo Objeto Atualizado

    var udpateditem = this.state.items.find(function(item) { 
                   return item.name == "field_1" });
    udpateditem.name= "New updated name"                       
    this.setState(prevState => ({                                   
    items:prevState.dl_name_template.filter(function(item) { 
                                    return item.name !== "field_1"}).concat(udpateditem)
    }));

0

Que tal criar outro componente (para o objeto que precisa ir para a matriz) e passar o seguinte como adereços?

  1. índice do componente - o índice será usado para criar / atualizar na matriz.
  2. função set - Esta função coloca os dados na matriz com base no índice do componente.
<SubObjectForm setData={this.setSubObjectData}                                                            objectIndex={index}/>

Aqui, {index} pode ser passado com base na posição em que este SubObjectForm é usado.

e setSubObjectData pode ser algo assim.

 setSubObjectData: function(index, data){
      var arrayFromParentObject= <retrieve from props or state>;
      var objectInArray= arrayFromParentObject.array[index];
      arrayFromParentObject.array[index] = Object.assign(objectInArray, data);
 }

No SubObjectForm, this.props.setData pode ser chamado na alteração de dados, conforme indicado abaixo.

<input type="text" name="name" onChange={(e) => this.props.setData(this.props.objectIndex,{name: e.target.value})}/>

0
this.setState({
      items: this.state.items.map((item,index) => {
        if (index === 1) {
          item.name = 'newName';
        }
        return item;
      })
    });

1
isso não é ideal, você itera toda a matriz para atualizar apenas o segundo item?
wscourge 8/04

Você também está mutando o elemento na primeira matriz também, você deve usaritem = Object.assign({}, item, {name: 'newName'});
remram

0

A resposta de @ JonnyBuchanan funciona perfeitamente, mas apenas para a variável de estado do array. Caso a variável state seja apenas um único dicionário, siga isto:

inputChange = input => e => {
    this.setState({
        item: update(this.state.item, {[input]: {$set: e.target.value}})
    })
}

Você pode substituir [input]pelo nome do campo do seu dicionário e e.target.valuepor seu valor. Este código executa o trabalho de atualização no evento de alteração de entrada do meu formulário.


-3

Tente isso, definitivamente funcionará, caso contrário, tentei, mas não funcionou

import _ from 'lodash';

this.state.var_name  = _.assign(this.state.var_name, {
   obj_prop: 'changed_value',
});

Nunca mude o estado do React diretamente.
Neurotransmissor

-3
 handleChanges = (value, key) => {
     // clone the current State object
    let cloneObject = _.extend({}, this.state.currentAttribute);
    // key as user.name and value= "ABC" then current attributes have current properties as we changes
    currentAttribute[key] = value;
    // then set the state "currentAttribute" is key and "cloneObject" is changed object.  
    this.setState({currentAttribute: cloneObject});

e Alterar da caixa de texto, evento add onChange

onChange = {
   (event) => {                                                
      this.handleChanges(event.target.value, "title");
   }
}
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.