Come aggiornare le proprietà dello stato nidificato in React


390

Sto cercando di organizzare il mio stato utilizzando la proprietà nidificata in questo modo:

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

Ma l'aggiornamento dello stato in questo modo,

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

non funziona Come può essere fatto correttamente?


4
Cosa vuoi dire non funziona? Questa non è un'ottima domanda: cosa è successo? Qualche errore? Quali errori?
Darren Sweeney,


16
La risposta è Non usare lo stato nidificato in React . Leggi questa risposta eccellente.
Qwerty,

4
Cosa dovrebbe essere usato invece?
Jānis Elmeris,

42
Semplicemente non usare lo stato nidificato è una risposta inaccettabile per quanto oggi sia ampiamente usato React. Questa situazione verrà fuori e gli sviluppatori hanno bisogno di una risposta a questo.
serraosays

Risposte:


456

Per setStateun oggetto nidificato è possibile seguire l'approccio seguente poiché penso che setState non gestisca gli aggiornamenti nidificati.

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

L'idea è quella di creare un oggetto fittizio eseguire operazioni su di esso e quindi sostituire lo stato del componente con l'oggetto aggiornato

Ora, l'operatore di diffusione crea solo una copia nidificata di livello dell'oggetto. Se il tuo stato è fortemente nidificato come:

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

È possibile impostareState utilizzando l'operatore spread ad ogni livello come

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

Comunque la sintassi sopra diventa brutta man mano che lo stato diventa sempre più nidificato e quindi ti consiglio di usare il immutability-helperpacchetto per aggiornare lo stato.

Vedi questa risposta su come aggiornare lo stato con immutability helper.


2
@Ohgodwhy, no, questo non accede direttamente allo stato poiché {... this.state.someProperty} ritira un nuovo obbiettivo somePropertye lo stiamo modificando
Shubham Khatri,

4
questo non ha funzionato per me .. sto usando la versione di reazione @ 15.6.1
Rajiv

5
@Stophface Possiamo usare Lodash per clonare in profondità, ma non tutti includerebbero questa libreria solo per impostareState
Shubham Khatri,

15
Questo non viola reajs.org/docs/… ? Se due modifiche all'oggetto nidificato venissero messe in batch, l'ultima modifica sovrascriverebbe la prima. Quindi il modo più corretto sarebbe: this.setState((prevState) => ({nested: {...prevState.nested, propertyToSet: newValue}}) Un po 'noioso ...
olejorgenb

2
Non credo che tu abbia bisogno ...prevState,dell'ultimo esempio, solo tu somePropertye i suoi figli
Jemar Jones,

140

Per scriverlo in una riga

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

12
Si consiglia di utilizzare il programma di aggiornamento per setState invece di accedere direttamente a this.state all'interno di setState. reactionjs.org/docs/react-component.html#setstate
Raghu Teja,

6
Credo che lo stia dicendo, non leggere this.statesubito dopo setStatee aspettati che abbia il nuovo valore. Contro chiamare this.statedentro setState. O c'è anche un problema con quest'ultimo che non mi è chiaro?
trpt4him,

2
Come diceva trpt4him , il link che hai dato parla del problema di accedere this.statedopo setState. Inoltre, non stiamo passando this.statea setState. L' operatore ha diffuso le proprietà. È lo stesso di Object.assign (). github.com/tc39/proposal-object-rest-spread
Yoseph

3
@RaghuTeja forse solo fare this.setState(currentState => { someProperty: { ...currentState.someProperty, flag: false} });potrebbe consentire di evitare questo problema?
Webwoman

Ho aggiunto la risposta su come ottenere lo stesso risultato con hook StateState per completezza.
Andrew,

90

A volte le risposte dirette non sono le migliori :)

Versione breve:

questo codice

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

dovrebbe essere semplificato come qualcosa del genere

this.state = {
    somePropertyFlag: true
}

Versione lunga:

Attualmente non dovresti voler lavorare con lo stato nidificato in React . Perché React non è orientato a lavorare con stati nidificati e tutte le soluzioni qui proposte sembrano hack. Non usano il framework ma combattono con esso. Suggeriscono di scrivere un codice non così chiaro per uno scopo dubbio di raggruppare alcune proprietà. Quindi sono molto interessanti come risposta alla sfida ma praticamente inutili.

Immaginiamo il seguente stato:

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

Cosa succederà se cambi solo un valore di child1? React non eseguirà nuovamente il rendering della vista perché utilizza un confronto superficiale e scoprirà che la parentproprietà non è cambiata. La BTW che muta direttamente l'oggetto di stato è considerata una cattiva pratica in generale.

Quindi è necessario ricreare l'intero parentoggetto. Ma in questo caso incontreremo un altro problema. React penserà che tutti i bambini hanno cambiato i loro valori e li ridistribuirà tutti. Ovviamente non è buono per le prestazioni.

È ancora possibile risolvere questo problema scrivendo una logica complicata, shouldComponentUpdate()ma preferirei fermarmi qui e utilizzare una soluzione semplice dalla versione breve.


4
Penso che ci siano casi d'uso in cui uno stato nidificato va perfettamente bene. Ad esempio uno stato che tiene traccia dinamica delle coppie chiave-valore. Vedi qui come è possibile aggiornare lo stato per una sola voce nello stato: reazionejs.org/docs/…
pors

1
Il confronto superficiale viene utilizzato per componenti puri per impostazione predefinita.
Basilea Shishani,

4
Voglio creare un altare in tuo onore e pregarlo ogni mattina. Mi ci sono voluti tre giorni per raggiungere questa risposta che spiega perfettamente la decisione progettuale OBVIOUS. Tutti cercano solo di usare l'operatore di diffusione o di fare altri hack solo perché lo stato nidificato sembra più leggibile dall'uomo.
Edeph,

1
Come suggerisci, quindi, di utilizzare React per eseguire operazioni come il rendering di un albero interattivo (ad esempio, la mia azienda ha un gruppo di sensori in diverse parti del mondo, ognuno di tipo diverso, con proprietà diverse, in posizioni diverse (80+ ) e OF COURSE sono organizzati in una gerarchia poiché ogni distribuzione costa migliaia di dollari ed è un progetto completo, quindi sarebbe impossibile gestirli senza una gerarchia). Sono piuttosto curioso di sapere come non modelleresti cose come alberi come ... alberi, in React.
DanyAlejandro,

10
sembra che React non sia stato creato per rappresentare tutto ciò che è memorizzato come una struttura ricorsiva di profondità non banale. È un po 'deludente poiché le viste ad albero compaiono in quasi tutte le applicazioni complesse (gmail, file manager, qualsiasi sistema di gestione dei documenti, ..). Ho usato React per queste cose, ma ho sempre avuto un fanatico React che diceva a tutti che stai facendo anti-pattern e suggerendo che la soluzione sta mantenendo copie profonde dell'albero nello stato di ogni nodo (sì, 80 copie). Mi piacerebbe vedere un componente di visualizzazione ad albero non hacky che non infrange alcuna regola di React.
DanyAlejandro,

63

disconoscimento

Lo stato nidificato in React ha un design errato

Leggi questa risposta eccellente .

 

Ragionamento dietro questa risposta:

SetState di React è solo una comodità integrata, ma presto ti rendi conto che ha i suoi limiti. L'uso di proprietà personalizzate e l'uso intelligente di forceUpdate ti offrono molto di più. per esempio:

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

MobX, ad esempio, i fossi dichiarano completamente e usano proprietà osservabili personalizzate.
Usa gli osservabili anziché lo stato nei componenti di React.

 


la risposta alla tua miseria - vedi esempio qui

Esiste un altro modo più breve per aggiornare qualsiasi proprietà nidificata.

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

Su una riga

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

nota: questo qui è Operatore virgola ~ MDN , vederlo in azione qui (Sandbox) .

È simile a (sebbene questo non cambi il riferimento di stato)

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

Per la sottile differenza in questo contesto tra forceUpdatee setStatevedere l'esempio collegato.

Naturalmente questo sta abusando di alcuni principi fondamentali, come statedovrebbe essere di sola lettura, ma poiché scarti immediatamente il vecchio stato e lo sostituisci con uno nuovo, è completamente ok.

avvertimento

Anche se il componente che contiene lo stato si aggiornerà e eseguirà nuovamente il rendering ( tranne questo gotcha ) , gli oggetti di scena non si propagheranno ai bambini (vedere il commento di Spymaster di seguito) . Usa questa tecnica solo se sai cosa stai facendo.

Ad esempio, puoi passare un puntello piatto modificato che viene aggiornato e passato facilmente.

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

Ora, anche se il riferimento per complexNestedProp non è cambiato ( shouldComponentUpdate )

this.props.complexNestedProp === nextProps.complexNestedProp

il componente eseguirà nuovamente il rendering ogni volta che il componente padre si aggiorna, come nel caso della chiamata this.setStateo this.forceUpdatenel padre.

Effetti della mutazione dello stato

Uso stato annidato e mutando direttamente lo stato è pericoloso perché diversi oggetti potrebbero tenere (intenzionalmente o meno) diversi (vecchi) riferimenti alla condizione e potrebbero non necessariamente sapere quando dell'aggiornamento (ad esempio quando si utilizza PureComponento se shouldComponentUpdateviene implementato per ritorno false) OR sono destinato a visualizzare vecchi dati come nell'esempio seguente.

Immagina una linea temporale che dovrebbe rendere i dati storici, la mutazione dei dati sotto la mano comporterà un comportamento imprevisto in quanto cambierà anche gli elementi precedenti.

state-flow Stato-flow-nested

Comunque qui puoi vedere che Nested PureChildClassnon è stato reso di nuovo a causa della mancata propagazione degli oggetti di scena.


12
Non dovresti mutare alcuno stato di reazione da nessuna parte. Se ci sono componenti nidificati che utilizzano dati da state.nested o state.another, non eseguiranno il rendering o i loro figli. Questo perché l'oggetto non è cambiato, quindi React pensa che nulla sia cambiato.
Zeragamba,

@ SpyMaster356 Ho aggiornato la mia risposta per risolvere questo problema. Si noti inoltre che MobX, ad esempio, elimina statecompletamente e utilizza proprietà osservabili personalizzate . React's setStateè solo una comodità integrata, ma presto ti rendi conto che ha i suoi limiti. L'uso di proprietà personalizzate e l'uso intelligente di forceUpdateti offre molto di più. Usa gli osservabili anziché lo stato nei componenti di React.
Qwerty,

Inoltre ho aggiunto un esempio reazioni-experimenti.herokuapp.com/state-flow (link a git in alto)
Qwerty

Cosa succede se si carica ad esempio un oggetto con attributi nidificati da un database in stato? Come evitare lo stato nidificato in quel caso?
Tobiv

3
@tobiv È meglio trasformare i dati al momento del recupero in una struttura diversa, ma dipende dal caso d'uso. Va bene avere un oggetto nidificato o una matrice di oggetti in uno stato se li si considera come unità compatte di alcuni dati. Il problema sorge quando è necessario archiviare switch dell'interfaccia utente o altri dati osservabili (ovvero dati che dovrebbero cambiare immediatamente la vista) in quanto devono essere piatti per attivare correttamente gli algoritmi di diffrazione di React interni. Per le modifiche in altri oggetti nidificati, è facile attivare this.forceUpdate()o implementare specifici shouldComponentUpdate.
Qwerty

21

Se si utilizza ES2015, si ha accesso a Object.assign. Puoi usarlo come segue per aggiornare un oggetto nidificato.

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

Unisci le proprietà aggiornate con quelle esistenti e usa l'oggetto restituito per aggiornare lo stato.

Modifica: aggiunto un oggetto vuoto come destinazione alla funzione di assegnazione per assicurarsi che lo stato non sia mutato direttamente come indicato da Carkod.


12
E potrei sbagliarmi ma non penso che tu stia usando React senza ES2015.
Madbreaks,

16
const newState = Object.assign({}, this.state);
newState.property.nestedProperty = "new value";
this.setState(newState);

1
sembra la soluzione più elegante qui, 1) evitando la noiosa diffusione all'interno di setState e 2) inserendo bene il nuovo stato all'interno di setState, evitando di mutare direttamente all'interno setState. Ultimo ma non meno importante 3) evitare l'uso di qualsiasi libreria per un semplice compito
Webwoman

I due esempi non sono equivalenti. Se propertycontiene diverse proprietà nidificate, la prima le eliminerà, la seconda no. codesandbox.io/s/nameless-pine-42thu
Qwerty

Qwerty, hai ragione, grazie per lo sforzo. Ho rimosso la versione ES6.
eyecandy.dev,

Semplice e diretto!
Tony Sepia,

Ho usato la tua soluzione. Il mio stato obj è piccolo e ha funzionato bene. React verrà visualizzato solo per lo stato modificato o eseguirà il rendering di tutto?
Kenji Noguchi,

14

Ci sono molte librerie per aiutarti in questo. Ad esempio, utilizzando 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);

Utilizzando lodash / fp merge:

import {merge} from 'lodash/fp';

const newState = merge(this.state, {
  someProperty: {flag: false},
});

Se lo usi lodash, probabilmente vorrai provare a _.cloneDeepimpostare lo stato come copia clonata.
Yixing Liu,

@YixingLiu: ho aggiornato la risposta da utilizzare lodash/fp, quindi non dobbiamo preoccuparci delle mutazioni.
tokland,

Qualche vantaggio nella scelta mergeo setnelle funzioni in lodash / fp oltre alla sintassi?
Toivo Säwén il

Buona risposta, forse desideri aggiungere che devi chiamare anche this.setState(newState)i metodi lodash / fp. Un altro gotcha è che lodash .set(non-fp) ha un diverso ordine di argomenti che mi ha fatto scattare per un po '.
Toivo Säwén,

7

Usiamo Immer https://github.com/mweststrate/immer per gestire questo tipo di problemi.

Ho appena sostituito questo codice in uno dei nostri componenti

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

Con questo

import produce from 'immer';

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

Con immer gestisci il tuo stato come un "oggetto normale". La magia accade dietro la scena con oggetti proxy.


6

Ecco una variazione sulla prima risposta data in questo thread che non richiede alcun pacchetto aggiuntivo, librerie o funzioni speciali.

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

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

Per impostare lo stato di uno specifico campo nidificato, è stato impostato l'intero oggetto. Ho fatto questo creando una variabile, newStatee diffondere il contenuto dello stato corrente in esso prima usando l'ES2015 operatore diffusione . Quindi, ho sostituito il valore di this.state.flagcon il nuovo valore (dal momento che ho impostato flag: value dopo aver diffuso lo stato corrente nell'oggetto, il flagcampo nello stato corrente viene sovrascritto). Quindi, ho semplicemente impostato lo stato del somePropertymio newStateoggetto.


6

Sebbene l'annidamento non sia proprio il modo in cui dovresti trattare lo stato di un componente, a volte per qualcosa di facile per l'annidamento a livello singolo.

Per uno stato come questo

state = {
 contact: {
  phone: '888-888-8888',
  email: 'test@test.com'
 }
 address: {
  street:''
 },
 occupation: {
 }
}

Un metodo riutilizzabile in uso sarebbe simile a questo.

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

quindi basta passare il nome obj per ogni annidamento che si desidera affrontare ...

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

4

Ho usato questa soluzione.

Se hai uno stato nidificato come questo:

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

è possibile dichiarare la funzione handleChange che copia lo stato corrente e lo riassegna con i valori modificati

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

qui l'html con il listener di eventi

<input type="text" onChange={this.handleChange} " name="friendName" />

Confermato. Funziona alla grande se hai già a che fare con un progetto già stabilito con proprietà nidificate.
metal_jacke1

4

Crea una copia dello stato:

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

apportare modifiche in questo oggetto:

someProperty.flag = "false"

ora aggiorna lo stato

this.setState({someProperty})

setState è asincrono. Quindi, mentre si aggiorna, qualcun altro può chiamare questa funzione e copiare una versione obsoleta dello stato.
Aviv Ben Shabat,

3

Sebbene tu abbia chiesto informazioni sullo stato del componente React basato sulla classe, lo stesso problema esiste con il hook useState. Ancora peggio: il gancio useState non accetta aggiornamenti parziali. Quindi questa domanda è diventata molto rilevante quando è stato introdotto il gancio useState.

Ho deciso di pubblicare la seguente risposta per assicurarmi che la domanda riguardi scenari più moderni in cui viene utilizzato il hook useState:

Se hai:

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

è possibile impostare la proprietà nidificata clonando l'attuale e correggendo i segmenti richiesti dei dati, ad esempio:

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

Oppure puoi usare la libreria Immer per semplificare la clonazione e la correzione dell'oggetto.

Oppure puoi utilizzare la libreria Hookstate (dichiarazione di non responsabilità: sono un autore) per semplicemente la gestione di dati di stato complessi (locali e globali) interamente e migliorare le prestazioni (leggi: non preoccuparti dell'ottimizzazione del rendering):

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

ottenere il campo per il rendering:

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

imposta il campo nidificato:

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

Ecco l'esempio di Hookstate, in cui lo stato è nidificato in modo ricorsivo / profondo nella struttura di dati ad albero .


2

Altre due opzioni non ancora menzionate:

  1. Se hai uno stato profondamente annidato, considera se puoi ristrutturare gli oggetti figlio in modo che siedano alla radice. Ciò semplifica l'aggiornamento dei dati.
  2. Ci sono molte utili librerie disponibili per la gestione dello stato immutabile elencate nei documenti Redux . Consiglio Immer in quanto consente di scrivere codice in modo mutativo ma gestisce la clonazione necessaria dietro le quinte. Inoltre congela l'oggetto risultante in modo da non poterlo mutare accidentalmente in un secondo momento.

2

Per rendere le cose generiche, ho lavorato sulle risposte di @ ShubhamKhatri e @ Qwerty.

oggetto di stato

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

controlli di input

<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"
/>

metodo updateState

setState come risposta di @ 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 come risposta di @ 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: questi metodi sopra non funzioneranno per le matrici


2

Prendo molto sul serio le preoccupazioni già espresse in merito alla creazione di una copia completa del tuo stato di componente. Detto questo, suggerirei vivamente Immer .

import produce from 'immer';

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

Questo dovrebbe funzionare per React.PureComponent(cioè confronti di stati superficiali di React) poiché Immerutilizza abilmente un oggetto proxy per copiare in modo efficiente un albero di stato arbitrariamente profondo. Immer è anche più versatile rispetto alle librerie come Immutability Helper ed è ideale sia per gli utenti Javascript che per quelli Typescript.


Funzione di utilità dattiloscritta

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

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

1
Non penso sia corretto, dato che objè solo un riferimento allo stato, quindi attraverso di esso stai mutando l' this.stateoggetto reale
Ciprian Tomoiagă

0

Ho scoperto che questo funziona per me, avendo una forma di progetto nel mio caso in cui ad esempio hai un ID e un nome e preferirei mantenere lo stato per un progetto nidificato.

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

Fammi sapere!


0

Qualcosa del genere potrebbe bastare,

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) 

0

So che è una vecchia domanda, ma volevo ancora condividere come ho raggiunto questo obiettivo. Supponendo che lo stato nel costruttore sia simile al seguente:

  constructor(props) {
    super(props);

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

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

La mia handleChangefunzione è così:

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

E assicurati di nominare gli input di conseguenza:

<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"
/>

0

Faccio aggiornamenti nidificati con una ricerca ridotta :

Esempio:

Le variabili nidificate nello stato:

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

La funzione:

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

Uso:

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

0

Questo è il mio stato iniziale

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

L'hook o è possibile sostituirlo con lo stato (componente di classe)

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

Il metodo per handleChange

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

Metodo per impostare lo stato con stati nidificati

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 questo è l'input che uso

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

0

Se stai usando formik nel tuo progetto, ha un modo semplice per gestire queste cose. Ecco il modo più semplice di fare con formik.

Innanzitutto imposta i tuoi valori iniziali all'interno dell'attributo formik initivalues ​​o nella reazione. stato

Qui, i valori iniziali sono definiti nello stato di reazione

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

definire sopra i valori iniziali per il campo formik all'interno initiValuesdell'attributo 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>

Crea una console per verificare lo stato in cui è stato aggiornato stringanziché booleanla setFieldValuefunzione formik per impostare lo stato o vai con lo strumento di debug di reazione per vedere le modifiche all'interno dei valori di stato di formik.


0

prova questo codice:

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

Questo rimuoverà qualsiasi altra proprietà presente nell'oggetto a cui fa riferimento somePropertye dopo l'esecuzione del codice precedente flagrimarrà solo la proprietà
Yashas

0

ho visto quanto segue in un libro:

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

ma non sono sicuro che sia giusto ..

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.