Cómo actualizar las propiedades de estado anidado en React

390

Estoy tratando de organizar mi estado usando propiedades anidadas como esta:

this.state = {
   someProperty: {
      flag:true
   }
}

Pero actualizar estado como este,

this.setState({ someProperty.flag: false });

no funciona ¿Cómo se puede hacer esto correctamente?

Alex Yong
fuente
44
¿Qué quieres decir con que no funciona? Esta no es una muy buena pregunta: ¿qué pasó? Cualquier error? Que errores
Darren Sweeney
Intenta leer: stackoverflow.com/questions/18933985/…
Andrew Paramoshkin el
16
La respuesta es No usar estado anidado en React . Lee esta excelente respuesta.
Qwerty
44
¿Qué se debe usar en su lugar?
Jānis Elmeris
42
Simplemente no usar el estado anidado es una respuesta inaceptable para lo ampliamente utilizado que hoy es React. Esta situación va a surgir y los desarrolladores necesitan una respuesta a esto.
serraosays

Respuestas:

456

Para setStateun objeto anidado, puede seguir el siguiente enfoque, ya que creo que setState no maneja las actualizaciones anidadas.

var someProperty = {...this.state.someProperty}
someProperty.flag = true;
this.setState({someProperty})

La idea es crear un objeto ficticio realizar operaciones en él y luego reemplazar el estado del componente con el objeto actualizado

Ahora, el operador de propagación crea solo una copia anidada de nivel del objeto. Si su estado está altamente anidado como:

this.state = {
   someProperty: {
      someOtherProperty: {
          anotherProperty: {
             flag: true
          }
          ..
      }
      ...
   }
   ...
}

Puede configurar State utilizando el operador de propagación en cada nivel como

this.setState(prevState => ({
    ...prevState,
    someProperty: {
        ...prevState.someProperty,
        someOtherProperty: {
            ...prevState.someProperty.someOtherProperty, 
            anotherProperty: {
               ...prevState.someProperty.someOtherProperty.anotherProperty,
               flag: false
            }
        }
    }
}))

Sin embargo, la sintaxis anterior se vuelve fea a medida que el estado se anida cada vez más y, por lo tanto, le recomiendo que use el immutability-helperpaquete para actualizar el estado.

Vea esta respuesta sobre cómo actualizar el estado con immutability helper.

Shubham Khatri
fuente
2
@ Ohhodwhy, no, esto no es acceder al estado directamente ya que {... this.state.someProperty} vuelve a ejecutar un nuevo objeto somePropertyy estamos modificando eso
Shubham Khatri
44
esto no funcionó para mí ... estoy usando la versión de reacción @ 15.6.1
Rajiv
55
@Stophface Podemos usar Lodash para clonar en profundidad, pero no todos incluirían esta biblioteca solo para establecer el estado
Shubham Khatri
15
¿No viola esto reactjs.org/docs/… ? Si dos cambios en el objeto anidado se agrupan, el último cambio sobrescribirá el primero. Entonces, la forma más correcta sería: this.setState((prevState) => ({nested: {...prevState.nested, propertyToSet: newValue}}) un poco tedioso ...
olejorgenb
2
No creo que necesites el ...prevState,en el último ejemplo, solo tú somePropertyy sus hijos
Jemar Jones
140

Para escribir en una línea

this.setState({ someProperty: { ...this.state.someProperty, flag: false} });
Yoseph
fuente
12
Se recomienda utilizar el actualizador para setState en lugar de acceder directamente a this.state dentro de setState. reactjs.org/docs/react-component.html#setstate
Raghu Teja
66
Creo que eso es decir, no lea this.stateinmediatamente después setStatey espere que tenga el nuevo valor. Versus llamando this.statedentro setState. ¿O también hay un problema con este último que no me queda claro?
trpt4him
2
Como dijo trpt4him , el enlace que diste habla sobre el problema de acceso this.statedespués setState. Además, no estamos pasando this.statea setState. El operador difunde propiedades. Es lo mismo que Object.assign (). github.com/tc39/proposal-object-rest-spread
Yoseph
3
¿@RaghuTeja tal vez solo this.setState(currentState => { someProperty: { ...currentState.someProperty, flag: false} });podría permitir evitar este problema?
Webwoman
He agregado la respuesta de cómo lograr lo mismo con useState hook para completar.
Andrew
90

A veces las respuestas directas no son las mejores :)

Version corta:

este codigo

this.state = {
    someProperty: {
        flag: true
    }
}

debería simplificarse como algo así

this.state = {
    somePropertyFlag: true
}

Versión larga:

Actualmente no debería querer trabajar con estado anidado en React . Porque React no está orientado a trabajar con estados anidados y todas las soluciones propuestas aquí parecen piratas informáticos. No usan el marco pero luchan con él. Sugieren escribir código no tan claro con el propósito dudoso de agrupar algunas propiedades. Por lo tanto, son muy interesantes como respuesta al desafío, pero prácticamente inútiles.

Imaginemos el siguiente estado:

{
    parent: {
        child1: 'value 1',
        child2: 'value 2',
        ...
        child100: 'value 100'
    }
}

¿Qué sucederá si cambia solo un valor de child1? React no volverá a representar la vista porque usa una comparación superficial y verá que la parentpropiedad no cambió. Por cierto, mutar el objeto de estado directamente se considera una mala práctica en general.

Por lo tanto, debe volver a crear todo el parentobjeto. Pero en este caso nos encontraremos con otro problema. Reaccionar pensará que todos los niños han cambiado sus valores y los volverá a representar a todos. Por supuesto, no es bueno para el rendimiento.

Todavía es posible resolver ese problema escribiendo una lógica complicada, shouldComponentUpdate()pero preferiría detenerme aquí y usar una solución simple de la versión corta.

Konstantin Smolyanin
fuente
44
Creo que hay casos de uso donde un estado anidado está perfectamente bien. Por ejemplo, un estado que realiza un seguimiento dinámico de pares clave-valor. Vea aquí cómo se puede actualizar el estado por sólo una única entrada en el estado: reactjs.org/docs/...
Pors
1
La comparación superficial se utiliza para componentes puros de forma predeterminada.
Basel Shishani
44
Quiero crear un altar en tu honor y rezar por él cada mañana. Me llevó tres días llegar a esta respuesta que explica perfectamente la decisión de diseño OBVIO. Todos intentan usar el operador de propagación o hacer otros hacks solo porque el estado anidado parece más legible para los humanos.
Edeph el
1
¿Cómo sugiere, entonces, utilizar React para hacer cosas como renderizar un árbol interactivo (por ejemplo, mi empresa tiene un montón de sensores en diferentes partes del mundo, cada uno de diferente tipo, con diferentes propiedades, en diferentes ubicaciones (80+ ), y por supuesto, están organizados en una jerarquía ya que cada implementación cuesta miles de dólares y es un proyecto completo, por lo que sería imposible administrarlos sin una jerarquía). Tengo curiosidad por saber cómo no modelarías cosas como árboles como ... árboles, en React.
DanyAlejandro
10
parece que React no se hizo para representar algo que se almacena como una estructura recursiva de profundidad no trivial. Es un poco decepcionante ya que las vistas de árbol aparecen en casi todas las aplicaciones complejas (gmail, administradores de archivos, cualquier sistema de administración de documentos, ...). He usado React para estas cosas, pero siempre tuve un fanático de React que les decía a todos que estaban haciendo antipatrones y sugirió que la solución es mantener copias profundas del árbol en el estado de cada nodo (sí, 80 copias). Me encantaría ver un componente de vista de árbol no hacky que no rompa ninguna regla de Reacción.
DanyAlejandro
63

Descargo de responsabilidad

El estado anidado en React es un diseño incorrecto

Lee esta excelente respuesta .

 

Razonamiento detrás de esta respuesta:

SetState de React es solo una conveniencia incorporada, pero pronto te das cuenta de que tiene sus límites. El uso de propiedades personalizadas y el uso inteligente de forceUpdate le brinda mucho más. p.ej:

class MyClass extends React.Component {
    myState = someObject
    inputValue = 42
...

MobX, por ejemplo, abandona el estado por completo y utiliza propiedades observables personalizadas.
Use Observables en lugar de estado en React components.

 


la respuesta a tu miseria - mira el ejemplo aquí

Hay otra forma más corta de actualizar cualquier propiedad anidada.

this.setState(state => {
  state.nested.flag = false
  state.another.deep.prop = true
  return state
})

En una linea

 this.setState(state => (state.nested.flag = false, state))

nota: Aquí está el operador de coma ~ MDN , vea aquí en acción (Sandbox) .

Es similar a (aunque esto no cambia la referencia de estado)

this.state.nested.flag = false
this.forceUpdate()

Para la sutil diferencia en este contexto entre forceUpdatey setStatever el ejemplo vinculado.

Por supuesto, esto está abusando de algunos principios básicos, ya que statedeberían ser de solo lectura, pero dado que descarta inmediatamente el estado anterior y lo reemplaza por un estado nuevo, está completamente bien.

Advertencia

A pesar de que el componente que contiene el estado se actualizará y volverá a mostrar correctamente ( excepto este problema ) , los accesorios no se propagarán a los niños (consulte el comentario de Spymaster a continuación) . Solo use esta técnica si sabe lo que está haciendo.

Por ejemplo, puede pasar un accesorio plano modificado que se actualiza y pasa fácilmente.

render(
  //some complex render with your nested state
  <ChildComponent complexNestedProp={this.state.nested} pleaseRerender={Math.random()}/>
)

Ahora, aunque la referencia para complexNestedProp no cambió ( shouldComponentUpdate )

this.props.complexNestedProp === nextProps.complexNestedProp

el componente se volverá a generar cada vez que se actualice el componente principal, que es el caso después de llamar this.setStateo this.forceUpdateen el elemento primario.

Efectos de mutar el estado

Usar el estado anidado y mutar el estado directamente es peligroso porque diferentes objetos pueden contener (intencionalmente o no) diferentes (más antiguas) referencias al estado y no necesariamente saber cuándo actualizar (por ejemplo, cuando se usa PureComponento si shouldComponentUpdatese implementa para regresar false) O son destinado a mostrar datos antiguos como en el ejemplo a continuación.

Imagine una línea de tiempo que se supone que representa datos históricos, mutar los datos debajo de la mano dará como resultado un comportamiento inesperado, ya que también cambiará los elementos anteriores.

flujo de estado estado-flujo-anidado

De todos modos, aquí puede ver que Nested PureChildClassno se reenvió debido a que los accesorios no se propagan.

QWERTY
fuente
12
No deberías estar mutando ningún estado de reacción en ningún lado. Si hay componentes anidados que usan datos de state.nested o state.another, no se volverán a procesar ni sus hijos. Esto se debe a que el objeto no cambió, por lo que React piensa que nada ha cambiado.
Zeragamba
@ SpyMaster356 He actualizado mi respuesta para abordar esto. También tenga en cuenta que MobX, por ejemplo, abandona statecompletamente y utiliza propiedades observables personalizadas . React's setStatees solo una conveniencia incorporada, pero pronto te das cuenta de que tiene sus límites. El uso de propiedades personalizadas y el uso inteligente de forceUpdatele brinda mucho más. Use Observables en lugar de estado en React components.
Qwerty
También agregué un ejemplo react-experiments.herokuapp.com/state-flow (enlace a git en la parte superior)
Qwerty
¿Qué sucede si carga, por ejemplo, un objeto con atributos anidados de una base de datos en estado? ¿Cómo evitar el estado anidado en ese caso?
tobiv
3
@tobiv Es mejor transformar los datos en una búsqueda en una estructura diferente, pero depende del caso de uso. Está bien tener un objeto anidado o una matriz de objetos en un estado si los considera unidades compactas de algunos datos. El problema surge cuando necesita almacenar interruptores de la interfaz de usuario u otros datos observables (es decir, datos que deberían cambiar inmediatamente la vista) ya que deben ser planos para activar correctamente los algoritmos internos React. Para cambios en otros objetos anidados, es fácil activar this.forceUpdate()o implementar específicos shouldComponentUpdate.
Qwerty
21

Si está utilizando ES2015, tiene acceso a Object.assign. Puede usarlo de la siguiente manera para actualizar un objeto anidado.

this.setState({
  someProperty: Object.assign({}, this.state.someProperty, {flag: false})
});

Combina las propiedades actualizadas con las existentes y utiliza el objeto devuelto para actualizar el estado.

Editar: se agregó un objeto vacío como objetivo a la función de asignación para asegurarse de que el estado no esté mutado directamente como señaló Carkod.

Alyssa Roose
fuente
12
Y podría estar equivocado, pero no creo que estés usando React sin ES2015.
Madbreaks
16
const newState = Object.assign({}, this.state);
newState.property.nestedProperty = "new value";
this.setState(newState);
eyecandy.dev
fuente
1
Parece la solución más elegante aquí, 1) evitar la tediosa propagación dentro de setState y 2) poner bien el nuevo estado dentro de setState, evitando mutar el setState directamente dentro. Por último, pero no menos importante 3) evitar el uso de cualquier biblioteca por sólo una tarea sencilla
webwoman
Los dos ejemplos no son equivalentes. Si propertycontiene varias propiedades anidadas, la primera las eliminará, la segunda no. codesandbox.io/s/nameless-pine-42thu
Qwerty
Qwerty, tienes razón, gracias por el esfuerzo. Eliminé la versión ES6.
eyecandy.dev
Directo y simple!
Tony Sepia
Usé tu solución. El obj de mi estado es pequeño y funcionó bien. ¿Reaccionará el procesamiento solo para el estado modificado o lo hará todo?
Kenji Noguchi
14

Hay muchas bibliotecas para ayudar con esto. Por ejemplo, usando immutability-helper :

import update from 'immutability-helper';

const newState = update(this.state, {
  someProperty: {flag: {$set: false}},
};
this.setState(newState);

Usando lodash / fp set:

import {set} from 'lodash/fp';

const newState = set(["someProperty", "flag"], false, this.state);

Usando lodash / fp merge:

import {merge} from 'lodash/fp';

const newState = merge(this.state, {
  someProperty: {flag: false},
});
tokland
fuente
Si usa lodash, probablemente quiera probar _.cloneDeepy establecer el estado para que sea la copia clonada.
Yixing Liu
@YixingLiu: He actualizado la respuesta para usar lodash/fp, así que no tenemos que preocuparnos por las mutaciones.
tokland
¿Alguna ventaja de elegir mergeo setfunciones en lodash / fp además de la sintaxis?
Toivo Säwén
Buena respuesta, tal vez desee agregar que también debe llamar this.setState(newState)a los métodos lodash / fp. Otro problema es que lodash .set(no fp) tiene un orden diferente de argumentos que me hizo tropezar por un tiempo.
Toivo Säwén
7

Utilizamos Immer https://github.com/mweststrate/immer para manejar este tipo de problemas.

Acabo de reemplazar este código en uno de nuestros componentes

this.setState(prevState => ({
   ...prevState,
        preferences: {
            ...prevState.preferences,
            [key]: newValue
        }
}));

Con este

import produce from 'immer';

this.setState(produce(draft => {
    draft.preferences[key] = newValue;
}));

Con immer manejas tu estado como un "objeto normal". La magia ocurre detrás de escena con objetos proxy.

Joakim Jäderberg
fuente
6

Aquí hay una variación en la primera respuesta dada en este hilo que no requiere ningún paquete adicional, bibliotecas o funciones especiales.

state = {
  someProperty: {
    flag: 'string'
  }
}

handleChange = (value) => {
  const newState = {...this.state.someProperty, flag: value}
  this.setState({ someProperty: newState })
}

Para establecer el estado de un campo anidado específico, ha establecido todo el objeto. Hice esto creando una variable newStatey difundiendo el contenido del estado actual primero usando el operador de propagación ES2015 . Luego, reemplacé el valor de this.state.flagcon el nuevo valor (ya que establecí flag: value después de extender el estado actual en el objeto, el flagcampo en el estado actual se anula). Luego, simplemente configuro el estado de somePropertymi newStateobjeto.

Matthew Berkompas
fuente
6

Aunque la anidación no es realmente cómo debe tratar un estado de componente, a veces es algo fácil para la anidación de un solo nivel.

Para un estado como este

state = {
 contact: {
  phone: '888-888-8888',
  email: '[email protected]'
 }
 address: {
  street:''
 },
 occupation: {
 }
}

Un método reutilizable que he usado se vería así.

handleChange = (obj) => e => {
  let x = this.state[obj];
  x[e.target.name] = e.target.value;
  this.setState({ [obj]: x });
};

luego simplemente pasa el nombre del obj para cada anidamiento que desea abordar ...

<TextField
 name="street"
 onChange={handleChange('address')}
 />
usuario2208124
fuente
4

Usé esta solución.

Si tiene un estado anidado como este:

   this.state = {
          formInputs:{
            friendName:{
              value:'',
              isValid:false,
              errorMsg:''
            },
            friendEmail:{
              value:'',
              isValid:false,
              errorMsg:''
            }
}

puede declarar la función handleChange que copia el estado actual y la reasigna con valores cambiados

handleChange(el) {
    let inputName = el.target.name;
    let inputValue = el.target.value;

    let statusCopy = Object.assign({}, this.state);
    statusCopy.formInputs[inputName].value = inputValue;

    this.setState(statusCopy);
  }

aquí el html con el oyente de eventos

<input type="text" onChange={this.handleChange} " name="friendName" />
Alberto Piras
fuente
Confirmado. Esto funciona muy bien si ya está lidiando con un proyecto ya establecido con propiedades anidadas.
metal_jacke1
4

Crea una copia del estado:

let someProperty = JSON.parse(JSON.stringify(this.state.someProperty))

hacer cambios en este objeto:

someProperty.flag = "false"

ahora actualiza el estado

this.setState({someProperty})
Dhakad
fuente
setState es asíncrono. Entonces, mientras se actualiza, alguien más puede llamar a esta función y copiar una versión obsoleta del estado.
Aviv Ben Shabat
3

Aunque usted preguntó sobre un estado del componente React basado en clases, el mismo problema existe con useState hook. Peor aún: useState hook no acepta actualizaciones parciales. Entonces, esta pregunta se volvió muy relevante cuando se introdujo el gancho useState.

He decidido publicar la siguiente respuesta para asegurarme de que la pregunta cubra escenarios más modernos en los que se usa el gancho useState:

Si tienes:

const [state, setState] = useState({ someProperty: { flag: true, otherNestedProp: 1 }, otherProp: 2 })

puede establecer la propiedad anidada clonando la actual y aplicando parches a los segmentos requeridos de los datos, por ejemplo:

setState(current => { ...current, someProperty: { ...current.someProperty, flag: false } });

O puede usar la biblioteca Immer para simplificar la clonación y el parcheo del objeto.

O puede usar la biblioteca Hookstate (descargo de responsabilidad: soy un autor) para simplemente administrar por completo los datos de estado complejos (locales y globales) y mejorar el rendimiento (lea: no se preocupe por la optimización del renderizado):

import { useStateLink } from '@hookstate/core' 
const state = useStateLink({ someProperty: { flag: true, otherNestedProp: 1 }, otherProp: 2 })

obtener el campo para representar:

state.nested.someProperty.nested.flag.get()
// or 
state.get().someProperty.flag

establecer el campo anidado:

state.nested.someProperty.nested.flag.set(false)

Aquí está el ejemplo de Hookstate, donde el estado está anidado profunda / recursivamente en una estructura de datos en forma de árbol .

Andrés
fuente
2

Otras dos opciones aún no mencionadas:

  1. Si tiene un estado profundamente anidado, considere si puede reestructurar los objetos secundarios para sentarse en la raíz. Esto hace que los datos sean más fáciles de actualizar.
  2. Hay muchas bibliotecas útiles disponibles para manejar el estado inmutable que figuran en los documentos de Redux . Recomiendo Immer ya que le permite escribir código de manera mutante pero maneja la clonación necesaria detrás de escena. También congela el objeto resultante para que no pueda mutarlo accidentalmente más tarde.
Casa Cory
fuente
2

Para hacer las cosas genéricas, trabajé en las respuestas de @ ShubhamKhatri y @ Qwerty.

objeto de estado

this.state = {
  name: '',
  grandParent: {
    parent1: {
      child: ''
    },
    parent2: {
      child: ''
    }
  }
};

controles de entrada

<input
  value={this.state.name}
  onChange={this.updateState}
  type="text"
  name="name"
/>
<input
  value={this.state.grandParent.parent1.child}
  onChange={this.updateState}
  type="text"
  name="grandParent.parent1.child"
/>
<input
  value={this.state.grandParent.parent2.child}
  onChange={this.updateState}
  type="text"
  name="grandParent.parent2.child"
/>

método updateState

setState como la respuesta de @ ShubhamKhatri

updateState(event) {
  const path = event.target.name.split('.');
  const depth = path.length;
  const oldstate = this.state;
  const newstate = { ...oldstate };
  let newStateLevel = newstate;
  let oldStateLevel = oldstate;

  for (let i = 0; i < depth; i += 1) {
    if (i === depth - 1) {
      newStateLevel[path[i]] = event.target.value;
    } else {
      newStateLevel[path[i]] = { ...oldStateLevel[path[i]] };
      oldStateLevel = oldStateLevel[path[i]];
      newStateLevel = newStateLevel[path[i]];
    }
  }
  this.setState(newstate);
}

setState como la respuesta de @ Qwerty

updateState(event) {
  const path = event.target.name.split('.');
  const depth = path.length;
  const state = { ...this.state };
  let ref = state;
  for (let i = 0; i < depth; i += 1) {
    if (i === depth - 1) {
      ref[path[i]] = event.target.value;
    } else {
      ref = ref[path[i]];
    }
  }
  this.setState(state);
}

Nota: estos métodos anteriores no funcionarán para matrices

Venugopal
fuente
2

Me tomo muy en serio las preocupaciones ya expresadas en torno a la creación de una copia completa del estado de su componente. Dicho esto, sugeriría encarecidamente a Immer .

import produce from 'immer';

<Input
  value={this.state.form.username}
  onChange={e => produce(this.state, s => { s.form.username = e.target.value }) } />

Esto debería funcionar React.PureComponent(es decir, las comparaciones de estado superficial por React), ya que Immerutiliza inteligentemente un objeto proxy para copiar eficientemente un árbol de estado arbitrariamente profundo. Immer también es más seguro para los tipos de letra en comparación con bibliotecas como Immutability Helper, y es ideal para usuarios de Javascript y Typecript por igual.


Función de utilidad mecanografiada

function setStateDeep<S>(comp: React.Component<any, S, any>, fn: (s: 
Draft<Readonly<S>>) => any) {
  comp.setState(produce(comp.state, s => { fn(s); }))
}

onChange={e => setStateDeep(this, s => s.form.username = e.target.value)}
Stephen Paul
fuente
1
stateUpdate = () => {
    let obj = this.state;
    if(this.props.v12_data.values.email) {
      obj.obj_v12.Customer.EmailAddress = this.props.v12_data.values.email
    }
    this.setState(obj)
}
usuario3444748
fuente
1
No creo que esto sea correcto, ya que objes solo una referencia al estado, por lo que a través de él estás mutando el this.stateobjeto real
Ciprian Tomoiagă
0

Encontré que esto funciona para mí, tener un formulario de proyecto en mi caso donde, por ejemplo, tiene una identificación y un nombre y prefiero mantener el estado de un proyecto anidado.

return (
  <div>
      <h2>Project Details</h2>
      <form>
        <Input label="ID" group type="number" value={this.state.project.id} onChange={(event) => this.setState({ project: {...this.state.project, id: event.target.value}})} />
        <Input label="Name" group type="text" value={this.state.project.name} onChange={(event) => this.setState({ project: {...this.state.project, name: event.target.value}})} />
      </form> 
  </div>
)

¡Házmelo saber!

Michael Stokes
fuente
0

Algo como esto podría ser suficiente,

const isObject = (thing) => {
    if(thing && 
        typeof thing === 'object' &&
        typeof thing !== null
        && !(Array.isArray(thing))
    ){
        return true;
    }
    return false;
}

/*
  Call with an array containing the path to the property you want to access
  And the current component/redux state.

  For example if we want to update `hello` within the following obj
  const obj = {
     somePrimitive:false,
     someNestedObj:{
        hello:1
     }
  }

  we would do :
  //clone the object
  const cloned = clone(['someNestedObj','hello'],obj)
  //Set the new value
  cloned.someNestedObj.hello = 5;

*/
const clone = (arr, state) => {
    let clonedObj = {...state}
    const originalObj = clonedObj;
    arr.forEach(property => {
        if(!(property in clonedObj)){
            throw new Error('State missing property')
        }

        if(isObject(clonedObj[property])){
            clonedObj[property] = {...originalObj[property]};
            clonedObj = clonedObj[property];
        }
    })
    return originalObj;
}

const nestedObj = {
    someProperty:true,
    someNestedObj:{
        someOtherProperty:true
    }
}

const clonedObj = clone(['someProperty'], nestedObj);
console.log(clonedObj === nestedObj) //returns false
console.log(clonedObj.someProperty === nestedObj.someProperty) //returns true
console.log(clonedObj.someNestedObj === nestedObj.someNestedObj) //returns true

console.log()
const clonedObj2 = clone(['someProperty','someNestedObj','someOtherProperty'], nestedObj);
console.log(clonedObj2 === nestedObj) // returns false
console.log(clonedObj2.someNestedObj === nestedObj.someNestedObj) //returns false
//returns true (doesn't attempt to clone because its primitive type)
console.log(clonedObj2.someNestedObj.someOtherProperty === nestedObj.someNestedObj.someOtherProperty) 
Eladian
fuente
0

Sé que es una vieja pregunta, pero aún quería compartir cómo lo logré. Asumiendo el estado en el constructor se ve así:

  constructor(props) {
    super(props);

    this.state = {
      loading: false,
      user: {
        email: ""
      },
      organization: {
        name: ""
      }
    };

    this.handleChange = this.handleChange.bind(this);
  }

Mi handleChangefunción es así:

  handleChange(e) {
    const names = e.target.name.split(".");
    const value = e.target.type === "checkbox" ? e.target.checked : e.target.value;
    this.setState((state) => {
      state[names[0]][names[1]] = value;
      return {[names[0]]: state[names[0]]};
    });
  }

Y asegúrese de nombrar las entradas en consecuencia:

<input
   type="text"
   name="user.email"
   onChange={this.handleChange}
   value={this.state.user.firstName}
   placeholder="Email Address"
/>

<input
   type="text"
   name="organization.name"
   onChange={this.handleChange}
   value={this.state.organization.name}
   placeholder="Organization Name"
/>
Elnoor
fuente
0

Hago actualizaciones anidadas con una búsqueda reducida :

Ejemplo:

Las variables anidadas en estado:

state = {
    coords: {
        x: 0,
        y: 0,
        z: 0
    }
}

La función:

handleChange = nestedAttr => event => {
  const { target: { value } } = event;
  const attrs = nestedAttr.split('.');

  let stateVar = this.state[attrs[0]];
  if(attrs.length>1)
    attrs.reduce((a,b,index,arr)=>{
      if(index==arr.length-1)
        a[b] = value;
      else if(a[b]!=null)
        return a[b]
      else
        return a;
    },stateVar);
  else
    stateVar = value;

  this.setState({[attrs[0]]: stateVar})
}

Utilizar:

<input
value={this.state.coords.x}
onChange={this.handleTextChange('coords.x')}
/>
Emisael Carrera
fuente
0

Este es mi estado inicial

    const initialStateInput = {
        cabeceraFamilia: {
            familia: '',
            direccion: '',
            telefonos: '',
            email: ''
        },
        motivoConsulta: '',
        fechaHora: '',
        corresponsables: [],
    }

El gancho o puede reemplazarlo con el estado (componente de clase)

const [infoAgendamiento, setInfoAgendamiento] = useState(initialStateInput);

El método para handleChange

const actualizarState = e => {
    const nameObjects = e.target.name.split('.');
    const newState = setStateNested(infoAgendamiento, nameObjects, e.target.value);
    setInfoAgendamiento({...newState});
};

Método para establecer el estado con estados anidados

const setStateNested = (state, nameObjects, value) => {
    let i = 0;
    let operativeState = state;
    if(nameObjects.length > 1){
        for (i = 0; i < nameObjects.length - 1; i++) {
            operativeState = operativeState[nameObjects[i]];
        }
    }
    operativeState[nameObjects[i]] = value;
    return state;
}

Finalmente esta es la entrada que uso

<input type="text" className="form-control" name="cabeceraFamilia.direccion" placeholder="Dirección" defaultValue={infoAgendamiento.cabeceraFamilia.direccion} onChange={actualizarState} />
Ramiro Carbonell Delgado
fuente
0

Si está utilizando formik en su proyecto, tiene una manera fácil de manejar estas cosas. Aquí está la forma más fácil de hacer con formik.

Primero establezca sus valores iniciales dentro del atributo formik initivalues ​​o en la reacción. estado

Aquí, los valores iniciales se definen en estado de reacción

   state = { 
     data: {
        fy: {
            active: "N"
        }
     }
   }

definir valores iniciales iniciales para el campo formik dentro del initiValuesatributo formik

<Formik
 initialValues={this.state.data}
 onSubmit={(values, actions)=> {...your actions goes here}}
>
{({ isSubmitting }) => (
  <Form>
    <Field type="checkbox" name="fy.active" onChange={(e) => {
      const value = e.target.checked;
      if(value) setFieldValue('fy.active', 'Y')
      else setFieldValue('fy.active', 'N')
    }}/>
  </Form>
)}
</Formik>

Haga una consola para verificar el estado actualizado en stringlugar de booleanla setFieldValuefunción formik para establecer el estado o vaya con la herramienta de depuración de reacción para ver los cambios dentro de los valores de estado formik.

Sharad Sharma
fuente
0

prueba este código:

this.setState({ someProperty: {flag: false} });
Soumya
fuente
Pero esto eliminará cualquier otra propiedad presente en el objeto referenciado por somePropertyy después de que se ejecute el código anterior, solo flagquedará la propiedad
Yashas
0

Vi lo siguiente en un libro:

this.setState(state => state.someProperty.falg = false);

pero no estoy seguro si es correcto ...

Eva húmeda
fuente