update subproperty react property only one array javascript reactjs

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



setstate array react (19)

¿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 datos en la matriz en función del índice del componente.

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

Aquí se puede pasar {index} en función de la posición donde se utiliza este SubObjectForm.

y setSubObjectData puede ser algo como esto.

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

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

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

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

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.setState para que actualice los items[1].name ?


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 los items una 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


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

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


De acuerdo con la documentación de React en setState , usar Object.assign como lo sugieren otras respuestas aquí no es ideal. Debido a la naturaleza del comportamiento asincrónico de setState , 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 usar la forma de actualización de setState que 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í:

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


Desplazaría el cambio del identificador de funciones y agregaría un parámetro de índice

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

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

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

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

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


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

let ItemsCopy = [] let x = this.state.Items.map((entry) =>{ if(entry.id == ''theIDYoureLookingFor'') { entry.PropertyToChange = ''NewProperty'' } ItemsCopy.push(entry) }) this.setState({Items:ItemsCopy});


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 }


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)



Para modificar objetos / variables profundamente anidados en el estado de React, generalmente se utilizan tres métodos: vanilla JS Object.assign , immutability-helper y cloneDeep de 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, hay algunos métodos que no usan nada más que JavaScript vainilla, 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.

Nota : estos ejemplos utilizan la forma más antigua para actualizar el estado, con componentes de clase y ciclos de vida. Newer Hooks API tiene una sintaxis diferente, pero la idea sigue siendo la misma. Eso significa que todos estos ejemplos siguen siendo válidos.

Dada esta estructura de estado:

state = { foo: { bar: ''initial value'' } }

1. Vanilla JavaScript''s Object.assign

(...essential imports) class App extends Component { state = { foo: { bar: ''initial value'' } } componentDidMount() { console.log(this.state.foo.bar) // initial value const foo = Object.assign({}, this.state.foo, { bar: ''further value'' }) console.log(this.state.foo.bar) // initial value this.setState({ foo }, () => { console.log(this.state.foo.bar) // further value }) } (...rest of code)

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 los elementos de nivel superior de este objeto ( state.foo ). Y sus valores ( state.foo.bar ) deben ser primitive (cadenas, números, booleanos).

En este ejemplo, estamos creando una nueva constante ( const foo... ), usando Object.assign , que crea un objeto vacío ( {} ), copia el objeto state.foo ( { bar: ''initial value'' } ) en y luego copia un objeto diferente { bar: ''further value'' } sobre él. Entonces, al final, la constante foo recién creada tendrá un valor de { bar: ''further value'' } ya que la propiedad de la bar se anuló. Este foo es un objeto completamente nuevo, que no está vinculado al objeto de estado, por lo que puede mutar según sea necesario y el estado no cambiará.

La última parte es usar setState() setter para reemplazar el state.foo original en el estado con un objeto foo recién creado.

Ahora imagine que tenemos un estado más profundo como state = { foo: { bar: { baz: ''initial value'' } } } . Podríamos intentar crear un nuevo objeto foo y Object.assign con los contenidos foo del estado, pero Object.assign no podrá copiar el valor de baz en este objeto foo recién creado, ya que baz está anidado demasiado profundamente. Todavía podría copiar la bar , como en el ejemplo anterior, pero dado que ahora es un objeto y no una primitiva, la referencia de state.foo.bar se copiará en su lugar, lo que significa que terminaremos con un objeto foo local directamente vinculado a el estado. Eso significa que en este caso cualquier mutación del foo creado localmente afectará al objeto state.foo , ya que de hecho apuntan a lo mismo.

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 contengan valores del tipo primitivo.

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

2. El clon de Lodash Profundo

(...essential imports) import cloneDeep from ''lodash.clonedeep'' class App extends Component { state = { foo: { bar: ''initial value'' } } componentDidMount() { console.log(this.state.foo.bar) // initial value const foo = cloneDeep(this.state.foo) foo.bar = ''further value'' console.log(this.state.foo.bar) // initial value this.setState({ foo }, () => { console.log(this.state.foo.bar) // further value }) } (...rest of code)

El clonDeep de cloneDeep 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. Simplemente cloneDeep() la propiedad de estado de nivel superior, mute la parte clonada lo que quiera y setState() en el estado.

3. ayudante de inmutabilidad

(...essential imports) import update from ''immutability-helper'' class App extends Component { state = { foo: { bar: ''initial value'' } }; componentDidMount() { console.log(this.state.foo.bar) // initial value const foo = update(this.state.foo, { bar: { $set: ''further value'' } }) console.log(this.state.foo.bar) // initial value this.setState({ foo }, () => { console.log(this.state.foo.bar) // further value }); } (...rest of code)

immutability-helper lleva a un nivel completamente nuevo, y lo bueno de esto es que no solo puede $set valores para establecer elementos, sino también $push , $splice , $merge (etc.). Aquí hay una lista de comandos disponibles.

Notas al margen

Nuevamente, tenga en cuenta que this.setState() solo modifica las propiedades de primer nivel del objeto de estado (propiedad foo en este caso), no las profundamente anidadas ( foo.bar ). Si se comportara de una manera diferente, esta pregunta no existiría.

Y, por cierto, this.setState({ foo }) es solo una abreviatura de this.setState({ foo: foo }) . Y () => { console.log(this.state.foo.bar) } justo después de { foo } es una devolución de llamada que se ejecuta inmediatamente después de que setState haya establecido el estado. Conveniente, si necesita hacer algunas cosas después de que hizo su trabajo (en nuestro caso, para mostrar el estado inmediatamente después de que se configuró).

¿Cuál es el adecuado para su proyecto?

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

Si manipulas un estado enorme y / o complejo , el clonDeep de cloneDeep es una buena 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 puede usarse para la manipulación de estados.

... o 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 manejar estos datos. Establecer un estado complejo en React no es una operación sencilla, y sugeriré encarecidamente que piense en diferentes enfoques.

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


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 pasando solo un objeto en getInitialState sería mucho más fácil si usara un objeto con clave.

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


Prueba con el código:

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


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


Puede usar el asistente de update 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 método del ciclo de vida shouldComponentUpdate() usando === , puede editar el estado directamente y obligar al componente a volver a renderizar; esto es efectivamente lo mismo que @ la respuesta de los protagonistas, ya que saca un objeto del estado y lo edita.

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 con retención de estado a un componente secundario que necesita activar un cambio de estado.


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

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

Es mejor actualizar el red-one en la Array siguiente manera:

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


Use el evento en handleChange para descubrir 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/


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

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


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

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)


¡Camino equivocado!

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

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

Me llevó 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, }, })); };