Reaccionar: ¿cómo actualizar state.item [1] en estado usando setState?


260

Estoy creando una aplicación donde el usuario puede diseñar su propio formulario. Por ejemplo, especifique el nombre del campo y los detalles de qué otras columnas deben incluirse.

El componente está disponible como JSFiddle aquí .

Mi estado inicial se ve así:

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

Quiero actualizar el estado cuando el usuario cambia cualquiera de los valores, pero tengo dificultades para apuntar al objeto correcto:

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

¿Cómo debo crear this.setStatepara que se actualice items[1].name?



1
¿Cuál es su respuesta seleccionada para esta pregunta?
Braian Mellor

Respuestas:


126

Podría usar el updateayudante de inmutabilidad para esto :

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

O si no le importa poder detectar cambios en este elemento en un shouldComponentUpdate()método de ciclo de vida utilizando ===, puede editar el estado directamente y obligar al componente a volver a renderizar; esto es efectivamente lo mismo que la respuesta de @limelights, ya que es sacar un objeto del estado y editarlo.

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

Adición posterior a la edición:

Consulte la lección de Comunicación de componentes simples de react-training para ver un ejemplo de cómo pasar una función de devolución de llamada de un componente primario en espera a un componente secundario que necesita desencadenar un cambio de estado.


126

Como hay mucha información errónea en este hilo, así es como puede hacerlo sin libs 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});
},

Puede combinar los pasos 2 y 3 si lo desea:

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

O puede hacer todo en una línea:

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

Nota: hice itemsuna matriz. OP usó un objeto. Sin embargo, los conceptos son los mismos.


Puedes ver lo que sucede en tu terminal / consola:

 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

55
@TranslucentCloud Oh, sí, los métodos de ayuda son definitivamente buenos, pero creo que todos deberían saber lo que está pasando bajo el capó :-)
mpen

¿Por qué necesitamos los pasos 2 y 3? ¿Por qué no podemos mutar el clon directamente después del primer paso?
Evmorov

1
@Evmorov Porque no es un clon profundo. El paso 1 es simplemente clonar la matriz, no los objetos dentro. En otras palabras, cada uno de los objetos dentro de la nueva matriz todavía "apunta" a los objetos existentes en la memoria: la mutación de uno mutará al otro (son uno y lo mismo). Vea también el items[0] === clone[0]bit en mi ejemplo de terminal en la parte inferior. =Verifica tres veces si los objetos se refieren a lo mismo.
mpen

Se vuelve más complicado si no conoce el índice del elemento en la matriz.
Ian Warburton

@IanWarburton No realmente. items.findIndex()debería hacer un trabajo corto de eso.
mpen

92

¡Camino equivocado!

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

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

Como lo señalaron muchos mejores desarrolladores en los comentarios: ¡ mutar el estado está mal!

Me tomó un tiempo resolver esto. Lo anterior funciona pero quita el poder de React. Por ejemplo componentDidUpdate, no verá esto como una actualización porque se modifica directamente.

Entonces la forma correcta sería:

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

25
ES6 solo porque estás usando "const"?
nkkollaw

49
¿No se llama items[1].role = e.target.valuedirectamente al estado mutante?
antony

28
estás mutando el estado, esto está totalmente en contra de la idea de mantener el estado inmutable como reaccionar sugerido. Esto puede causarle mucho dolor en una aplicación grande.
ncubica

8
@MarvinVK, su respuesta dice "Entonces, la mejor práctica sería:" seguido del uso de "this.forceUpdate ();" lo cual no se recomienda ya que podría ser sobrescrito por setState (), consulte facebook.github.io/react/docs/react-component.html#state . Lo mejor es cambiar esto para que no sea confuso para los futuros lectores.
James Z.

8
Solo quería señalar que muchos de los comentarios son irrelevantes ahora debido a las ediciones y la forma correcta es en realidad la forma correcta.
heez

50

Para modificar objetos / variables profundamente anidados en el estado de React, generalmente se utilizan tres métodos: JavaScript de vainilla Object.assign, ayudante de inmutabilidad y cloneDeepde Lodash .

También hay muchas otras librerías de terceros menos populares para lograr esto, pero en esta respuesta, cubriré solo estas tres opciones. Además, existen algunos métodos JavaScript de vainilla adicionales, como la distribución de matrices (consulte la respuesta de @ mpen, por ejemplo), pero no son muy intuitivos, fáciles de usar y capaces de manejar todas las situaciones de manipulación de estado.

Como se señaló innumerables veces en los comentarios más votados a las respuestas, cuyos autores proponen una mutación directa del estado: simplemente no hagas eso . Este es un anti-patrón de Reacción ubicuo, que inevitablemente conducirá a consecuencias no deseadas. Aprende de la manera correcta.

Comparemos tres métodos ampliamente utilizados.

Dada esta estructura de objeto de estado:

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

Puede usar los siguientes métodos para actualizar el innervalor del campo más interno sin afectar el resto del estado.

1. Vanilla JavaScript Object.assign

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>

Tenga en cuenta que Object.assign no realizará una clonación profunda , ya que solo copia los valores de propiedad , y es por eso que lo que se llama una copia superficial (ver comentarios).

Para que esto funcione, solo debemos manipular las propiedades de los tipos primitivos ( outer.inner), es decir, cadenas, números, booleanos.

En este ejemplo, estamos creando una nueva constante ( const newOuter...), usando Object.assign, que crea un objeto vacío ( {}), copia el outerobjeto ( { inner: 'initial value' }) en él y luego copia un objeto diferente { inner: 'updated value' } sobre él.

De esta manera, al final, la newOuterconstante recién creada mantendrá un valor { inner: 'updated value' }desde que la innerpropiedad se anuló. Este newOuteres un objeto nuevo, que no está vinculado al objeto en estado, por lo que puede mutarse según sea necesario y el estado permanecerá igual y no cambiará hasta que se ejecute el comando para actualizarlo.

La última parte es usar setOuter()setter para reemplazar el original outeren el estado con un newOuterobjeto recién creado (solo cambiará el valor, no cambiará el nombre de la propiedad outer).

Ahora imagine que tenemos un estado más profundo como state = { outer: { inner: { innerMost: 'initial value' } } }. Podríamos intentar crear el newOuterobjeto y llenarlo con los outercontenidos del estado, pero Object.assignno podremos copiar innerMostel valor de este newOuterobjeto recién creado ya que innerMostestá anidado demasiado profundamente.

Todavía podría copiar inner, como en el ejemplo anterior, pero como ahora es un objeto y no un primitivo, la referencia de newOuter.innerse copiará en su outer.innerlugar, lo que significa que terminaremos con un newOuterobjeto local directamente vinculado al objeto en el estado .

Eso significa que en este caso las mutaciones de lo creado localmente newOuter.innerafectarán directamente al outer.innerobjeto (en estado), ya que de hecho se convirtieron en lo mismo (en la memoria de la computadora).

Object.assign por lo tanto, solo funcionará si tiene una estructura de estado profundo de un nivel relativamente simple con miembros más internos que tengan valores del tipo primitivo.

Si tiene objetos más profundos (segundo nivel o más), que debe actualizar, no los use Object.assign. Corre el riesgo de mutar directamente.

2. El clon de Lodash Profundo

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>

El clonDeep de Lodash es mucho más simple de usar. Realiza una clonación profunda , por lo que es una opción sólida, si tiene un estado bastante complejo con objetos o matrices de varios niveles dentro. Solo cloneDeep()la propiedad de estado de nivel superior, mute la parte clonada de la forma que desee y setOuter()regrese al estado.

3. ayudante de inmutabilidad

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-helperlo lleva a un nivel completamente nuevo, y el fresco de todo es que no sólo se $setvalora a los elementos estatales, sino también $push, $splice, $merge(etc.) ellos. Aquí hay una lista de comandos disponibles.

Notas al margen

Nuevamente, tenga en cuenta que eso setOutersolo modifica las propiedades de primer nivel del objeto de estado ( outeren estos ejemplos), no las profundamente anidadas ( outer.inner). Si se comportara de manera diferente, esta pregunta no existiría.

¿Cuál es el adecuado para su proyecto?

Si no quiere o no puede usar dependencias externas , y tiene una estructura de estado simple , siga Object.assign.

Si manipulas un estado enorme y / o complejo , Lodash's cloneDeepes una buena elección.

Si necesita capacidades avanzadas , es decir, si su estructura de estado es compleja y necesita realizar todo tipo de operaciones, intente immutability-helper, es una herramienta muy avanzada que puede usarse para la manipulación de estados.

... o, ¿ realmente necesitas hacer esto?

Si tiene datos complejos en el estado de React, quizás este sea un buen momento para pensar en otras formas de manejarlos. Establecer un objeto de estado complejo en los componentes React no es una operación sencilla, y sugiero encarecidamente que piense en diferentes enfoques.

Lo más probable es que sea mejor que guarde sus datos complejos en una tienda Redux, configurándolos allí usando reductores y / o sagas y accediendo a ellos usando selectores.


Object.assignNo realiza una copia profunda. Digamos, si a = {c: {d: 1}}y b = Object.assign({}, a), entonces ejecutas b.c.d = 4, entonces a.c.destá mutado.
Awol

Tienes razón, el valor 1del objeto más interno ( a.c.d) se mutará. Pero si reasigna el sucesor de primer nivel de b, de esta manera:, b.c = {f: 1}la parte correspondiente de ano se mutará (permanecerá {d: 1}). Buena captura de todos modos, actualizaré la respuesta de inmediato.
Neurotransmisor el

Lo que ha definido es en realidad una shallow copyy no una deep copy. Es fácil confundir lo que shallow copysignifica. En shallow copy, a !== bpero para cada clave del objeto de origen a,a[key] === b[key]
Awol

Sí, mencioné explícitamente la superficialidad de Object.assignen la respuesta.
Neurotransmisor

JSON.parse(JSON.stringify(object))También es una variante para el clon profundo. Sin cloneDeepembargo, el rendimiento es peor que lodash . measurethat.net/Benchmarks/Show/2751/0/…
tylik

35

Yo tuve el mismo problema. ¡Aquí hay una solución simple que funciona!

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

11
@TranslucentCloud: esto ciertamente no es una mutación directa. la matriz original fue clonada, modificada y luego el estado se estableció nuevamente usando la matriz clonada.
vsync

@vsync sí, ahora después de editar la respuesta original, esto no es una mutación en absoluto.
Neurotransmisor

2
@TranslucentCloud - Antes también, mi edición no tenía nada que ver con eso, muchas gracias por la actitud. @Jonas aquí solo cometió un simple error en su respuesta, usando {llaves en lugar de llaves que había remediado
vsync

31

De acuerdo con la documentación de React en setState , usar Object.assigncomo lo sugieren otras respuestas aquí no es lo ideal. Debido a la naturaleza del setStatecomportamiento asincrónico, las llamadas posteriores que utilizan esta técnica pueden anular las llamadas anteriores y causar resultados no deseados.

En cambio, los documentos de React recomiendan utilizar la forma de actualización setStateque opera en el estado anterior. Tenga en cuenta que al actualizar una matriz u objeto debe devolver una nueva matriz u objeto, ya que React requiere que mantengamos la inmutabilidad del estado. El uso del operador de propagación de la sintaxis ES6 para copiar superficialmente una matriz, creando o actualizando una propiedad de un objeto en un índice dado de la matriz se vería así:

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

2
Esta es la respuesta apropiada si usa ES6. Es en línea lo que @Jonas ha respondido. Sin embargo, debido a la explicación, se destaca.
Sourabh

Sí, este código funciona perfectamente bien y es muy simple ...
Shoeb Mirza

29

Primero obtenga el elemento que desea, cambie lo que desea en ese objeto y vuelva a configurarlo en el estado. La forma en que usa el estado al pasar solo un objeto getInitialStatesería mucho más fácil si usara un objeto con clave.

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

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

3
No, se rinde Uncaught TypeError: Cannot read property 'items' of null.
Martins

No, no lo hará. Su error probablemente se deba a la forma en que lo está haciendo getInitialState.
Henrik Andersson

10
@HenrikAndersson Algo no parece correcto en tu ejemplo. itemsno está definido en ningún lado.
Edward D'Souza

1
@ EdwardD'Souza ¡Tienes toda la razón! Mi respuesta es mostrar cómo se debe definir y usar. La forma en que se configura el código de los solicitantes no funciona para lo que le gustaría, razón por la cual se requiere la necesidad de un objeto con clave.
Henrik Andersson

77
Este es un antipatrón React típico, está asignando itemuna referencia a la propia this.state.items[1]variable del estado . Luego modificas item( item.name = 'newName') y, por lo tanto, mutas el estado directamente, lo cual es altamente desaconsejado. En su ejemplo, incluso no hay necesidad de llamar this.setState({items: items}), porque el estado ya está mutado directamente.
Neurotransmisor

22

No mutes el estado en su lugar. Puede causar resultados inesperados. ¡He aprendido mi lección! Trabajar siempre con una copia / clon, Object.assign()es una buena:

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
¿Qué hay itemsen tu ejemplo? ¿Quiso decir this.state.itemso algo más?
Buh Buh

Probé esto pero le falta una línea. Arriba items[1] = item;debería haber una línea que diga items = this.state.items;. Cuidado, mi JavaScript está oxidado y estoy aprendiendo a reaccionar para mi proyecto de casa, así que no tengo idea de si esto es bueno o malo :-)
Greg0ry

4

Es muy simple

Primero, extraiga todo el objeto de elementos del estado, actualice la parte del objeto de elementos como desee y vuelva a poner todo el objeto de elementos en estado a través de 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á si tiene una estructura de estado profundo de un nivel relativamente simple con miembros más internos que tengan valores de tipo primitivo".
Neurotransmisor el

3

Como ninguna de las opciones anteriores era ideal para mí, terminé usando el mapa:

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

2

Mutación libre:

// 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
No puedo ver dónde newItemsse establece.
Neurotransmisor el

¿No es un mapa más una comparación en la matriz horrible para el rendimiento?
Natassia Tavares

@NatassiaTavares ... ¿qué? quizás te confunden fo ofo el forEachmapa es el más rápido.
Deano

1

Encontré esto sorprendentemente difícil y ninguno de los ES6 extendió la magia parecía funcionar como se esperaba. Estaba usando una estructura como esta para obtener propiedades de elementos renderizados para fines de diseño.

Se encontró que el uso del updatemétodo from immutability-helperes el más sencillo en este ejemplo 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 } } }))
  }

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

del miembro de la matriz que se va a actualizar es un objeto complejo más anidado. Utilice el método de copia profunda apropiado basado en la complejidad.

Seguramente hay mejores formas de manejar los parámetros de diseño, pero se trata de cómo manejar las matrices. Los valores relevantes para cada elemento secundario también se podrían calcular fuera de ellos, pero me pareció más conveniente pasar containerState hacia abajo, para que los elementos secundarios puedan obtener propiedades a voluntad y actualizar la matriz de estado principal en su índice dado.

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 el mapa de matriz con la función de flecha, en una línea

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

1
Esto es básicamente idéntico a esta otra respuesta publicada hace aproximadamente un año
CertainPerformance


0

Desplazaría el cambio de identificador de función y agregaría un parámetro de índice

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

al componente de formulario Dinámico y páselo al componente PopulateAtCheckboxes como accesorio. A medida que recorre sus elementos, puede incluir un contador adicional (llamado índice en el código a continuación) para pasar al cambio de identificador como se muestra a continuación

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

Finalmente, puede llamar a su escucha de cambios como se muestra a continuación aquí

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

Nunca mutes el estado de React directamente.
Neurotransmisor

0

Si necesita cambiar solo una parte de Array, tiene un componente de reacción con el estado establecido en.

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

Es mejor actualizar el red-oneen el Arraysiguiente:

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)

lo que es newArray? Qué quiere decir newItems? Si lo hace, ¿no dejaría eso al estado con un solo elemento después?
micnil

Esto introducirá una nueva propiedad newItemspara el stateobjeto y no actualizará la itemspropiedad existente .
Neurotransmisor el

0

o si tiene una lista generada dinámicamente y no conoce el índice pero solo tiene la clave o la identificación:

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

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

    ItemsCopy.push(entry)
})


this.setState({Items:ItemsCopy});

0

Prueba con el código:

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

this.setState({items: cloneObj });

0

El siguiente fragmento de código fue fácil para mi cerebro aburrido. Eliminar el objeto y reemplazarlo por el actualizado

    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

¿Qué tal crear otro componente (para el objeto que necesita ir a la matriz) y pasar lo siguiente como accesorios?

  1. índice de componente: el índice se utilizará para crear / actualizar en la matriz.
  2. Función set: esta función coloca los datos en la matriz en función del índice del componente.
<SubObjectForm setData={this.setSubObjectData}                                                            objectIndex={index}/>

Aquí se puede pasar {index} según la posición donde se usa este SubObjectForm.

y setSubObjectData puede ser algo como esto.

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

En SubObjectForm, this.props.setData se puede invocar en el cambio de datos como se indica a continuación.

<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
esto no es óptimo, ¿iteras toda la matriz para actualizar solo el segundo elemento?
wscourge

También está mutando el elemento en la primera matriz, debe usaritem = Object.assign({}, item, {name: 'newName'});
remram

0

La respuesta de @ JonnyBuchanan funciona perfectamente, pero solo para la variable de estado de la matriz. En caso de que la variable de estado sea solo un diccionario, siga esto:

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

Puede reemplazarlo [input]por el nombre del campo de su diccionario y e.target.valuepor su valor. Este código realiza el trabajo de actualización en el evento de cambio de entrada de mi formulario.


-3

Prueba esto, definitivamente funcionará, en otro caso lo intenté pero no funcionó

import _ from 'lodash';

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

Nunca mutes el estado de React directamente.
Neurotransmisor

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

y cambiar desde el cuadro de texto agregar evento onChange

onChange = {
   (event) => {                                                
      this.handleChanges(event.target.value, "title");
   }
}
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.