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

358

Estoy creando una aplicación donde el usuario puede diseñar su propio formulario. Por ejemplo, especifique el nombre del campo y 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 alguno de los valores, pero me cuesta 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 diseñar this.setStatepara que se actualice items[1].name?

2
305

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

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 está pasando 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
10
  • 6
    @TranslucentCloud Oh, sí, los métodos de ayuda son definitivamente agradables, pero creo que todos deberían saber qué está pasando debajo del capó :-)
    mpen
    5/04/18 a las 20:01
  • 1
    ¿Por qué necesitamos los pasos 2 y 3? ¿Por qué no podemos mutar el clon directamente después del primer paso?
    Evmorov
    5 dic. 18 a las 6:09
  • 5
    @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 - mutando uno mutará al otro (son uno y el 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
    5/12/18 a las 22:12
  • 1
    Se vuelve más complicado si no conoce el índice del elemento en la matriz. 10/06/19 a las 14:58
  • 3
    @IanWarburton No realmente. items.findIndex()debería hacer un trabajo corto de eso.
    mpen
    10/06/19 a las 21:58
134

Puede 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 forzar 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 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 elemento principal que retiene el estado a un componente secundario que necesita desencadenar un cambio de estado.

2
  • Al leer esta respuesta: stackoverflow.com/a/51018315/2396744 , creo que aún necesita el formulario de actualización de la función setstate, que usa la devolución de llamada prevstate.
    isgoed
    16/10/20 a las 12:52
  • Muchas gracias, Jonny, desperdiciar un día y finalmente obtener solución. 5 de marzo a las 6:34
106

¡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 darme cuenta de 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,
        },
    }));
};
18
  • 26
    ¿ES6 solo porque estás usando "const"? 21/07/2016 a las 15:34
  • 51
    ¿No es llamar items[1].role = e.target.valuedirectamente al estado mutante?
    antony
    20/0317 a las 17:33
  • 30
    estás mutando el estado, esto está totalmente en contra de la idea de mantener el estado inmutable como sugirió reaccionar. Esto puede causarle mucho dolor en una aplicación grande.
    ncubica
    21/04/2017 a las 1:26
  • 8
    @MarvinVK, su respuesta dice "Así que la mejor práctica sería:" seguido del uso de "this.forceUpdate ();" que no se recomienda ya que setState () podría sobrescribirlo, consulte facebook.github.io/react/docs/react-component.html#state . Es mejor cambiar esto para que no sea confuso para los futuros lectores. 1 de agosto de 2017 a las 7:27
  • 15
    Solo quería señalar que muchos de los comentarios ahora son irrelevantes debido a las ediciones y que la forma correcta es en realidad la forma correcta.
    heez
    13/01/19 a las 15:42
56

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

También hay muchas otras bibliotecas de terceros menos populares para lograr esto, pero en esta respuesta, cubriré solo estas tres opciones. Además, existen algunos métodos de JavaScript vanilla 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 estados.

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 React omnipresente, que inevitablemente conducirá a consecuencias no deseadas. Aprenda de la manera correcta.

Comparemos tres métodos ampliamente utilizados.

Dada esta estructura de objeto de estado:

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

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

1. Object.assign de Vanilla JavaScript

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 valores de propiedad , y por eso lo que hace se llama 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 tendrá un valor de { inner: 'updated value' }desde que innerse anuló la propiedad. Este newOuteres un objeto nuevo, que no está vinculado al objeto en estado, por lo que se puede mutar 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 el nombre de la propiedad outer).

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

Aún puede copiar inner, como en el ejemplo anterior, pero dado que ahora es un objeto y no una primitiva, 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 contienen 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 el estado directamente.

2. CloneDeep 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>

CloneDeep de Lodash es mucho más simple de usar. Realiza una clonación profunda , por lo que es una opción robusta, si tiene un estado bastante complejo con objetos o matrices de varios niveles en su interior. Solo cloneDeep()la propiedad de estado de nivel superior, mute la parte clonada de la forma que desee y setOuter()vuelva 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/[email protected]"></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 tu proyecto?

Si no desea o no puede usar dependencias externas y tiene una estructura de estado simple , apéguese a Object.assign.

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

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

... o, ¿ realmente necesitas hacer esto?

Si tiene datos complejos en el estado de React, tal vez este sea un buen momento para pensar en otras formas de manejarlos. Establecer un estado complejo de objetos en los componentes de React no es una operación sencilla, y sugiero encarecidamente pensar 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 acceda a ellos usando selectores.

7
  • Object.assignno realiza una copia profunda. Diga, si a = {c: {d: 1}}y b = Object.assign({}, a), luego ejecuta b.c.d = 4, luego a.c.destá mutado.
    Awol
    7 nov 2017 a las 20:06
  • Tienes razón, el valor 1del objeto más interno ( a.c.d) cambiará. Pero si reasigna el sucesor de primer nivel de b, así:, b.c = {f: 1}la parte correspondiente de ano se mutará (permanecerá {d: 1}). Buena captura de todos modos, actualizaré la respuesta de inmediato. 7/11/2017 a las 20:23
  • Lo que ha definido es en realidad un shallow copyy no un 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
    8 de noviembre de 2017 a las 12:26
  • Sí, mencionó explícitamente la superficialidad de Object.assignen la respuesta. 20/02/18 a las 11:44
  • JSON.parse(JSON.stringify(object))también es una variante del clon profundo. Sin cloneDeepembargo, el rendimiento es peor que lodash . Measurethat.net/Benchmarks/Show/2751/0/…
    tylik
    24/04/19 a las 10:44
42

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 });
3
  • 11
    @TranslucentCloud: esto ciertamente no es una mutación directa. la matriz original se clonó, se modificó y luego se volvió a establecer el estado utilizando la matriz clonada.
    vsync
    16/07/18 a las 14:51
  • @vsync sí, ahora, después de editar la respuesta original, esto no es una mutación en absoluto. 18/07/18 a las 13:58
  • 2
    @TranslucentCloud - Antes también, mi edición no tiene 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 solucionado
    vsync
    18/07/18 a las 16:19
36

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

En cambio, los documentos de React recomiendan usar 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 preservemos la inmutabilidad del estado. Usando el operador de propagación de la sintaxis de ES6 para copiar superficialmente una matriz, la creación o actualización de 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
  • 2
    Esta es la respuesta adecuada si usa ES6. Está en línea lo que ha respondido @Jonas. Sin embargo, debido a la explicación, se destaca.
    Sourabh
    13/06/19 a las 14:19
  • Sí, este código funciona perfectamente bien y es muy simple ... 7/10/19 a las 10:24
28

Primero obtenga el elemento que desea, cambie lo que desea en ese objeto y vuelva a establecerlo 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});
}
9
  • 3
    No, cede Uncaught TypeError: Cannot read property 'items' of null.
    martins
    9/04/2015 a las 11:40
  • No, no lo hará. Lo más probable es que su error se deba a la forma en que lo está haciendo getInitialState. 9/04/2015 a las 11:40
  • 10
    @HenrikAndersson Algo no parece correcto en tu ejemplo. itemsno está definido en ninguna parte. 15/06/2016 a las 17:14
  • 1
    @ EdwardD'Souza ¡Tienes toda la razón! Mi respuesta es mostrar cómo se debe definir y utilizar. La forma en que se configura el código de los solicitantes no funciona para lo que le gustaría, por lo que se requiere la necesidad de un objeto con clave. 15 de junio de 2016 a las 21:43
  • 7
    Este es un anti-patrón típico de React, estás 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 muy desaconsejado. En su ejemplo, ni siquiera es necesario llamar this.setState({items: items}), porque el estado ya está mutado directamente. 22/10/2017 a las 10:54
22

No mutes el estado en el lugar. Puede provocar resultados inesperados. ¡He aprendido mi lección! Trabajar siempre con una copia / clon, Object.assign()es 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

2
  • 8
    ¿Qué hay itemsen tu ejemplo? ¿Quisiste decir this.state.itemso algo más?
    Buh Buh
    6 feb 2018 a las 11:27
  • 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 el proyecto de mi casa, así que no tengo idea si esto es bueno o malo :-)
    Greg0ry
    6/10/19 a las 16:20
6

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

this.setState({items: this.state.items.map((item,idx)=> idx!==1 ?item :{...item,name:'new_name'}) })
1
  • ¿No es esto lo mismo que comentó @eicksl? 7 ago.20 a las 19:12
5

Usar mapa de matriz con función de flecha, en una línea

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

Libre de mutación:

// 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)
3
  • 3
    No puedo ver dónde newItemsestá establecido. 31/01/18 a las 14:56
  • ¿No es un mapa más una comparación en la matriz horrible para el rendimiento? 7/08/18 a las 19:44
  • @NatassiaTavares .. ¿qué? tal vez te confunden con el mapa fo ofo forEaches el más rápido.
    Deano
    5 de septiembre de 2019 a las 16:23
3

Es realmente 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
}
1
  • "Object.assign funcionará si tiene una estructura de estado profundo de un nivel relativamente simple con miembros más internos que contienen valores de tipo primitivo". 5/11/18 a las 11:14
3

A veces, en React, la mutación de la matriz clonada puede afectar la original, este método nunca causará una mutación:

    const myNewArray = Object.assign([...myArray], {
        [index]: myNewItem
    });
    setState({ myArray: myNewArray });

O si solo desea actualizar una propiedad de un elemento:

    const myNewArray = Object.assign([...myArray], {
        [index]: {
            ...myArray[index],
            prop: myNewValue
        }
    });
    setState({ myArray: myNewArray });
1

Encontré esto sorprendentemente difícil y nada de la magia de propagación de ES6 pareció funcionar como se esperaba. Estaba usando una estructura como esta para obtener propiedades de elementos renderizados para propósitos de diseño.

encontrado usando el updatemétodo de immutability-helperpara ser 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 actualizará es un objeto complejo más anidado, use el método de copia profunda apropiado en función de la complejidad.

Seguramente hay mejores formas de manejar los parámetros de diseño, pero se trata de cómo manejar matrices. Los valores relevantes para cada elemento secundario también podrían calcularse fuera de ellos, pero me pareció más conveniente pasar containerState hacia abajo, para que los niños puedan buscar propiedades a voluntad y actualizar la matriz del 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>
    )
  }
}
0

Utilice el evento handleChangepara averiguar el elemento que ha cambiado y luego actualícelo. Para eso, es posible que deba cambiar alguna propiedad para identificarla y actualizarla.

Ver violín https://jsfiddle.net/69z2wepo/6164/

0
0

Movería el cambio de manejador de la 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 pasarlo al componente PopulateAtCheckboxes como un accesorio. A medida que recorre sus artículos, 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 oyente 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"/>
1
  • Nunca mutes el estado de React directamente. 22/10/2017 a las 11:03
0

Si necesita cambiar solo una parte del 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-onede la Arraysiguiente manera:

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)
2
  • lo que es newArray? ¿te refieres newItems? Si lo hace, ¿no dejaría eso al estado con un solo artículo después?
    micnil
    18 oct 2017 a las 17:45
  • Esto introducirá una nueva propiedad newItemsen el stateobjeto y no actualizará la itemspropiedad existente . 17 de enero de 2018 a las 10:09
0

o si tiene una lista generada dinámicamente y no conoce el índice pero solo tiene la clave 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

Prueba con el código:

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

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

Seguir el código fue fácil para mi torpe cerebro. Eliminar el objeto y reemplazarlo con 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. set function: esta función coloca datos en la matriz según el índice del componente.
<SubObjectForm setData={this.setSubObjectData}                                                            objectIndex={index}/>

Aquí se puede pasar {index} en función de la posición en la que se utilice 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, se puede llamar a this.props.setData 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;
      })
    });
2
  • 1
    esto no es tan óptimo, ¿iteras toda la matriz para actualizar solo el segundo elemento? 8 abr.20 a las 11:45
  • También está mutando el elemento en la primera matriz también, debe usar item = Object.assign({}, item, {name: 'newName'});
    remram
    17/06/20 a las 16:16
0

La respuesta de @ JonnyBuchanan funciona perfectamente, pero solo para la variable de estado de 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 reemplazar [input]por el nombre de 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
 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 el cuadro Cambiar desde el texto agrega el evento onChange

onChange = {
   (event) => {                                                
      this.handleChanges(event.target.value, "title");
   }
}
-4

Pruebe 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',
});
1
  • Nunca mutes el estado de React directamente. 22/10/2017 a las 10:57