Cum să actualizați proprietățile de stare imbricate în React (Programare, Javascript, Reactjs, Ecmascript 6, Setstate)

Alex Yong a intrebat.

Încerc să-mi organizez starea folosind proprietăți imbricate ca aceasta:

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

Dar actualizarea stării ca aceasta,

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

nu funcționează. Cum se poate face acest lucru corect?

Comentarii

  • Ce vrei să spui că nu funcționează? Aceasta nu este o întrebare foarte bună – ce s-a întâmplat? Vreo eroare? Ce erori? –  > Por StudioTime.
  • Încearcă să citești: stackoverflow.com/questions/18933985/… -…  > Por Andrew Paramoshkin.
  • 20

  • Răspunsul este Nu folosiți Nested State în React. Citiți acest răspuns excelent. –  > Por Qwerty.
  • Ce ar trebui să fie folosit în schimb? –  > Por Jānis Elmeris.
  • 101

  • Simpla neutilizare a stării imbricate este un răspuns inacceptabil pentru cât de utilizat este React în prezent. Această situație va apărea, iar dezvoltatorii au nevoie de un răspuns la această problemă. –  > Por serraosays.
29 răspunsuri
Shubham Khatri

Pentru a setState pentru un obiect imbricate puteți urma abordarea de mai jos, deoarece cred că setState nu gestionează actualizările imbricate.

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

Ideea este de a crea un obiect fictiv, de a efectua operații asupra acestuia și apoi de a înlocui starea componentei cu obiectul actualizat

Acum, operatorul de răspândire creează doar o copie imbricata pe un singur nivel a obiectului. Dacă starea dvs. este foarte imbricată, cum ar fi:

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

Ați putea seta starea folosind operatorul de răspândire la fiecare nivel, de exemplu

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

Cu toate acestea, sintaxa de mai sus devine din ce în ce mai urâtă pe măsură ce starea devine din ce în ce mai anidată și, prin urmare, vă recomand să folosiți immutability-helper pentru a actualiza starea.

Consultați acest răspuns despre cum să actualizați starea cu immutability helper.

Comentarii

  • @Ohgodwhy , nu, acest lucru nu înseamnă accesarea directă a stării, deoarece {…this.state.someProperty} returnează un nou obiectiv pentru someProperty iar noi modificăm acel –  > Por Shubham Khatri.
  • acest lucru nu a funcționat pentru mine… eu folosesc versiunea react @15.6.1 –  > Por Rajiv.
  • @Stophface Putem folosi Lodash pentru a clona în profunzime, sigur, dar nu toată lumea ar include această bibliotecă doar pentru a setaState –  > Por Shubham Khatri.
  • 25

  • Nu încalcă acest lucru reactjs.org/docs/… ? În cazul în care două modificări ale obiectului imbricate au fost grupate, ultima modificare ar suprascrie prima. Așadar, cea mai corectă cale ar fi: this.setState((prevState) => ({nested: {...prevState.nested, propertyToSet: newValue}}) Un pic plictisitor, totuși… –  > Por olejorgenb.
  • Nu cred că aveți nevoie de ...prevState, în ultimul exemplu, ci doar someProperty și copiii săi –  > Por Jemar Jones.
Yoseph

Pentru a scrie într-un singur rând

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

Comentarii

    15

  • Se recomandă utilizarea updater-ului pentru setState în loc de accesarea directă a this.state în cadrul setState. reactjs.org/docs/react-component.html#setstate –  > Por Raghu Teja.
  • Cred că asta înseamnă că nu citiți this.state imediat după setState și să vă așteptați să aibă noua valoare. În comparație cu apelarea this.state în cadrul setState. Sau există o problemă cu aceasta din urmă care nu îmi este clară? –  > Por trpt4him.
  • După cum a spus trpt4him, link-ul pe care l-ai dat vorbește despre problema accesării this.state după setState. În plus, noi nu trecem this.state la setState. a răspândit proprietăți. Este același lucru cu Object.assign(). github.com/tc39/proposal-object-rest-spread –  > Por Yoseph.
  • @RaghuTeja poate că doar făcând this.setState(currentState => { someProperty: { ...currentState.someProperty, flag: false} }); ar putea permite evitarea acestei probleme? –  > Por Webwoman.
  • Am adăugat răspunsul cum să realizez același lucru cu cârligul useState pentru complezență. –  > Por Andrew.
Konstantin Smolyanin

Uneori, răspunsurile directe nu sunt cele mai bune 🙂

Versiunea scurtă:

acest cod

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

ar trebui să fie simplificat ca ceva de genul

this.state = {
    somePropertyFlag: true
}

Versiunea lungă:

În prezent nu ar trebui să doriți să lucrați cu state imbricate în React. Pentru că React nu este orientat să lucreze cu stări imbricate și toate soluțiile propuse aici arată ca niște hack-uri. Ele nu folosesc cadrul, ci se luptă cu el. Ele sugerează să scrieți un cod nu atât de clar pentru scopul îndoielnic de a grupa unele proprietăți. Așadar, acestea sunt foarte interesante ca răspuns la provocare, dar practic inutile.

Să ne imaginăm următoarea stare:

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

Ce se va întâmpla dacă modificați doar o valoare din child1? React nu va reda vizualizarea, deoarece utilizează o comparație superficială și va constata că parent proprietatea nu s-a schimbat. BTW, modificarea directă a obiectului de stare este considerată o practică proastă în general.

Așadar, trebuie să recreezi întregul obiect parent obiect. Dar în acest caz vom întâmpina o altă problemă. React va crede că toți copiii și-au schimbat valorile și îi va reda din nou pe toți. Desigur, acest lucru nu este bun pentru performanță.

Este totuși posibil să se rezolve această problemă prin scrierea unei logici complicate în shouldComponentUpdate() dar aș prefera să mă opresc aici și să folosesc soluția simplă din versiunea scurtă.

Comentarii

  • Cred că există cazuri de utilizare în care o stare imbricata este perfect bună. De exemplu, o stare care ține evidența dinamică a perechilor cheie-valoare. Vedeți aici cum puteți actualiza starea doar pentru o singură intrare în stare: reactjs.org/docs/… –  > Por pors.
  • Vreau să creez un altar în cinstea ta și să mă rog la el în fiecare dimineață. Mi-a luat trei zile pentru a ajunge la acest răspuns care explică perfect decizia OBLIGATORIE de proiectare. Toată lumea încearcă doar să folosească operatorul de răspândire sau să facă alte hack-uri doar pentru că starea imbricata arată mai ușor de citit pentru oameni. –  > Por Edeph.
  • Cum sugerați, atunci, să folosim React pentru a face lucruri precum redarea unui arbore interactiv (de exemplu, compania mea are o grămadă de senzori în diferite părți ale lumii, fiecare de tip diferit, cu proprietăți diferite, în locații diferite (80+) și, ÎN MOD NORMAL, sunt organizați într-o ierarhie, deoarece fiecare implementare costă mii de dolari și este un proiect complet, deci ar fi imposibil să le gestionăm fără o ierarhie). Sunt destul de curios cum de nu ai putea modela lucruri precum copacii ca… copaci, în React. –  > Por DanyAlejandro.
  • @DanyAlejandro ok, poate că ești unul dintre cei 0,1% de oameni care au nevoie de copac în starea React. Nu am spus că este strict interzis să faci așa ceva, am spus doar că aplicația ta poate deveni lentă și/sau complicată din cauza asta. Și dacă puteți folosi o stare plată, ar trebui să faceți asta. –  > Por Konstantin Smolyanin.
  • 19

  • se pare că React nu a fost făcut pentru a reprezenta ceva care este stocat ca o structură recursivă de adâncime non-trivială. Este un pic dezamăgitor, în condițiile în care tree-view-urile apar în aproape toate aplicațiile complexe de pe piață (gmail, managerii de fișiere, orice sistem de management al documentelor,..). Am folosit React pentru aceste lucruri, dar întotdeauna am avut un zelot React care spunea tuturor că faci anti-patterns și sugera că soluția este păstrarea unor copii profunde ale arborelui în starea fiecărui nod (da, 80 de copii). Mi-ar plăcea să văd o componentă de vizualizare a arborelui care să nu încalce nicio regulă React. –  > Por DanyAlejandro.
Qwerty

Disclaimer

Nested State în React este un design greșit

Citiți acest răspuns excelent.

 

Raționamentul din spatele acestui răspuns:

React’s setState este doar o comoditate încorporată, dar în curând vă dați seama că are limitele sale. Utilizarea proprietăților personalizate și utilizarea inteligentă aforceUpdate vă oferă mult mai mult.ex:

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

MobX, de exemplu, renunță complet la stat și folosește proprietăți observabile personalizate.
Folosiți Observables în loc de state în componentele React.

 


răspunsul la mizeria ta – vedeți un exemplu aici

Există o altă mai scurt mod mai scurt de a actualiza orice proprietate imbricata.

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

Pe o singură linie

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

notă: Aici este Operatorul de virgulă ~MDN, vedeți-l în acțiune aici (Sandbox).

Este similar cu (deși acesta nu schimbă referința de stare)

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

Pentru diferența subtilă în acest context între forceUpdate și setState consultați linkul exemplu și sandbox.

Desigur, acest lucru reprezintă un abuz de unele principii de bază, deoarece state ar trebui să fie numai pentru citire, dar, din moment ce se elimină imediat starea veche și se înlocuiește cu o stare nouă, este complet în regulă.

Avertisment

Chiar dacă componenta care conține starea va fi se va actualiza și se va reda în mod corespunzător (cu excepția acestui obstacol)propulsoarele vor fi eșuează să se propage la copii (a se vedea comentariul lui Spymaster de mai jos). Folosiți această tehnică numai dacă știți ce faceți.

De exemplu, este posibil să transmiteți un prop plat modificat care este actualizat și transmis cu ușurință.

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

Acum, chiar dacă referința pentru complexNestedProp nu s-a schimbat (shouldComponentUpdate)

this.props.complexNestedProp === nextProps.complexNestedProp

componenta va fi se va reda ori de câte ori componenta părinte se actualizează, ceea ce se întâmplă după apelarea componentei this.setState sau this.forceUpdate în componenta părinte.

Efectele modificării stării sandbox

Utilizarea starea imbricata și modificarea directă a stării este periculoasă, deoarece diferite obiecte pot deține (intenționat sau nu) referințe diferite (mai vechi) la starea stare și ar putea să nu știe neapărat când să se actualizeze (de exemplu, atunci când se utilizează PureComponent sau dacă shouldComponentUpdate este implementată pentru a returna false) SAU sunt destinate să afișeze date vechi, ca în exemplul de mai jos.

Imaginați-vă o linie de timp care ar trebui să redea date istorice, mutarea datelor sub mână va avea ca rezultat un comportament neașteptat, deoarece se vor modifica și elementele anterioare.

În orice caz, aici puteți vedea că Nested PureChildClass nu este redat din cauza eșecului de propagare a elementelor de recuzită.

Comentarii

  • Nu ar trebui să modificați nicăieri nicio stare react. Dacă există componente imbricate care utilizează date din state.nested sau state.another, acestea nu se vor reda și nici copiii lor. Acest lucru se datorează faptului că obiectul nu s-a schimbat, astfel că React crede că nu s-a schimbat nimic. –  > Por Zeragamba.
  • @tobiv Este mai bine să transformați datele în momentul unui fetch într-o structură diferită, dar depinde de cazul de utilizare. Este în regulă să aveți un obiect sau o matrice de obiecte imbricate într-o stare, dacă vă gândiți la ele ca la unități compacte de unele date. Problema apare atunci când trebuie să stocați comutatoare UI sau alte date observabile (adică date care ar trebui să schimbe imediat vizualizarea), deoarece acestea trebuie să fie plate pentru a declanșa corect algoritmii interni de diferențiere React. Pentru modificările în alte obiecte imbricate, este ușor să declanșați this.forceUpdate() sau să se implementeze o funcție specifică shouldComponentUpdate. –  > Por Qwerty.
  • Un pic offtop – ați putea furniza câteva informații de ce (expresia1, expresia2) în this.setState(state => (state.nested.flag = false, state)) funcționează în acest fel? (docs, mdn, cerere de căutare etc.) –  > Por Mega Proger.
  • @MegaProger Acesta este un Operatorul de virgulă ~MDN. Și, de fapt, returnând doar un obiect gol (exp1, {}) în setState ar funcționa, de asemenea, dar nu am vrut să creez noi obiecte în mod inutil. sandbox –  > Por Qwerty.
  • Așadar, ar putea funcționa mai bine să aveți o întreagă structură în afara statului, dar să folosiți statul pentru a face legătura cu zona specifică a structurii pe care trebuie să o accesați la un moment dat. –  > Por kojow7.
Alyssa Roose

Dacă utilizați ES2015, aveți acces la Object.assign. Îl puteți utiliza după cum urmează pentru a actualiza un obiect imbricate.

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

Îmbinați proprietățile actualizate cu cele existente și utilizați obiectul returnat pentru a actualiza starea.

Editare: Am adăugat un obiect gol ca țintă la funcția assign pentru a vă asigura că starea nu este mutată direct, așa cum a subliniat carkod.

Comentarii

  • Și s-ar putea să mă înșel, dar nu cred că folosești React. fără ES2015. –  > Por Madbreaks.
alphablend.dev
const newState = Object.assign({}, this.state);
newState.property.nestedProperty = "new value";
this.setState(newState);

Comentarii

  • pare a fi soluția mai elegantă aici, 1) evitând răspândirea plictisitoare în interiorul setState și 2) punând frumos noua stare în interiorul setState, evitând să mute direct în interiorul setState. Nu în ultimul rând 3) evitarea utilizării oricărei biblioteci pentru o sarcină simplă –  > Por Webwoman.
  • Cele două exemple nu sunt echivalente. În cazul în care property conține mai multe proprietăți imbricate, primul le va șterge, al doilea nu. codesandbox.io/s/nameless-pine-42thu –  > Por Qwerty.
  • Qwerty, aveți dreptate, mulțumesc pentru efort. Am eliminat versiunea ES6. –  > Por alphablend.dev.
  • Direct și simplu! –  > Por Tony Sepia.
  • Am folosit soluția ta. Obj-ul meu de stat este mic și a funcționat bine. React va reda doar pentru starea modificată sau va reda totul? –  > Por Kenji Noguchi.
tokland

Există multe biblioteci care să vă ajute în acest sens. De exemplu, folosind immutability-helper:

import update from 'immutability-helper';

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

Utilizarea lodash/fp set:

import {set} from 'lodash/fp';

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

Folosind lodash/fp fuzionează:

import {merge} from 'lodash/fp';

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

Comentarii

  • Dacă utilizați lodash, probabil doriți să încercați _.cloneDeep și să setați starea ca fiind copia clonată. –  > Por Yixing Liu.
  • @YixingLiu: Am actualizat răspunsul pentru a utiliza lodash/fp, astfel încât să nu trebuie să ne facem griji cu privire la mutații. –  > Por tokland.
  • Vreun avantaj în a alege merge sau set funcții în lodash/fp în afară de sintaxă? –  > Por Toivo Säwén.
  • Răspuns bun, poate că doriți să adăugați că trebuie să apelați this.setState(newState) și pentru metodele lodash/fp. O altă problemă este că lodash .set (non-fp) are o ordine diferită a argumentelor, ceea ce m-a încurcat pentru o vreme. –  > Por Toivo Säwén.
  • Merge are un comportament ciudat, atunci când se apelează this.setState(newState), creează un alt element de stare numit newState în this.state. Aveți vreo idee de ce? –  > Por Barry Chapman.
Joakim Jäderberg

Noi folosim Immer https://github.com/mweststrate/immer pentru a gestiona acest tip de probleme.

Tocmai am înlocuit acest cod într-una dintre componentele noastre

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

cu acest

import produce from 'immer';

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

Cu immer, vă gestionați starea ca un „obiect normal”.Magia se întâmplă în spatele scenei cu obiectele proxy.

Matthew Berkompas

Iată o variantă a primului răspuns dat în acest fir de discuție, care nu necesită pachete suplimentare, biblioteci sau funcții speciale.

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

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

Pentru a seta starea unui anumit câmp aninat, ați setat întregul obiect. Am făcut acest lucru prin crearea unei variabile, newState și răspândind conținutul stării curente în ea primul utilizând ES2015 operatorul spread. Apoi, am înlocuit valoarea din this.state.flag cu noua valoare (din moment ce am setat flag: value după am răspândit starea curentă în obiect, iar flag câmp din starea curentă este suprascrisă). Apoi, am setat pur și simplu starea lui someProperty la valoarea mea newState obiect.

user2208124

Cu toate că anveloparea nu este chiar modul în care ar trebui să tratați starea unei componente, uneori pentru ceva ușor pentru anveloparea pe un singur nivel.

Pentru o stare ca aceasta

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

O metodă reutilizabilă pe care am folosit-o ar arăta astfel.

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

apoi doar trecând numele obiectului pentru fiecare cuibare pe care doriți să o abordați…

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

Alberto Piras

Am folosit această soluție.

Dacă aveți o stare imbricata ca aceasta:

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

puteți declara funcția handleChange care copiază starea curentă și o realocă cu valorile modificate.

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

aici este html-ul cu ascultătorul de evenimente

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

Comentarii

  • Confirmat. Acest lucru funcționează foarte bine dacă aveți de-a face cu un proiect deja stabilit cu proprietăți imbricate. –  > Por metal_jacke1.
Dhakad

Creați o copie a stării:

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

faceți modificări în acest obiect:

someProperty.flag = "false"

acum actualizați starea

this.setState({someProperty})

Comentarii

  • setState este asincronă. Deci, în timp ce se actualizează, altcineva poate apela această funcție și poate copia o versiune depășită a stării. –  > Por Aviv Ben Shabat.
Andrew

Deși ați întrebat despre o stare a unei componente React bazate pe clasă, aceeași problemă există și în cazul cârligului useState. Chiar mai rău: cârligul useState nu acceptă actualizări parțiale. Așadar, această întrebare a devenit foarte relevantă atunci când a fost introdus cârligul useState.

Am decis să postez următorul răspuns pentru a mă asigura că întrebarea acoperă mai multe scenarii moderne în care se utilizează cârligul useState:

Dacă ați primit:

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

puteți seta proprietatea imbricata prin clonarea curentului și prin modificarea segmentelor de date necesare, de exemplu:

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

Sau puteți utiliza biblioteca Immer pentru a simplifica clonarea și modificarea obiectului.

Sau puteți utiliza biblioteca Hookstate (declarație de renunțare: sunt autor) pentru a simplifica în întregime gestionarea datelor de stare complexe (locale și globale) și pentru a îmbunătăți performanța (a se citi: să nu vă faceți griji cu privire la optimizarea randării):

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

obțineți câmpul de redat:

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

setați câmpul imbricate:

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

Iată exemplul Hookstate, în care starea este profund / recursiv imbricata în o structură de date de tip arbore.

Cory House

Alte două opțiuni care nu au fost menționate încă:

  1. Dacă aveți o stare profund imbricata, gândiți-vă dacă puteți restructura obiectele copil pentru a sta la rădăcină. Acest lucru face ca datele să fie mai ușor de actualizat.
  2. Există multe biblioteci utile disponibile pentru gestionarea stării imuabile enumerate în documentația Redux. Eu recomand Immer, deoarece vă permite să scrieți codul într-o manieră mutativă, dar gestionează clonarea necesară în spatele scenei. De asemenea, îngheață obiectul rezultat, astfel încât să nu îl puteți muta accidental mai târziu.

Venugopal

Pentru a face lucrurile generice, am lucrat la răspunsurile lui @ShubhamKhatri și @Qwerty.

obiect de stare

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

controale de intrare

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

Metoda updateState

setState ca răspuns al lui @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 ca răspuns al lui @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);
}

Notă: Aceste metode de mai sus nu vor funcționa pentru array-uri

Stephen Paul

Iau foarte în serios preocupările deja exprimate cu privire la crearea unei copii complete a stării componentei. Acestea fiind spuse, aș sugera cu tărie Immergeți.

import produce from 'immer';

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

Acest lucru ar trebui să funcționeze pentru React.PureComponent (adică comparații de stare superficiale de către React), deoarece Immer utilizează în mod inteligent un obiect proxy pentru a copia eficient un arbore de stare arbitrar de adânc. Immer este, de asemenea, mai sigur din punct de vedere al tipurilor în comparație cu biblioteci precum Immutability Helper și este ideal pentru utilizatorii de Javascript și Typescript deopotrivă.


Typescript funcție utilitară

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

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

Comentarii

  • Nu cred că acest lucru este corect, deoarece obj este doar o referință la stare, deci prin intermediul ei se modifică starea reală this.state obiect –  > Por Ciprian Tomoiagă.
DragonKnight

Este clar că nu este cea mai bună sau cea mai corectă metodă, totuși este mai curată după părerea mea:

this.state.hugeNestedObject = hugeNestedObject; 
this.state.anotherHugeNestedObject = anotherHugeNestedObject; 

this.setState({})

Cu toate acestea, React însuși ar trebui să itereze gândit obiecte imbricate și să actualizeze starea și DOM în consecință, ceea ce nu există încă.

Michael Stokes

Am constatat că acest lucru funcționează pentru mine, având un formular de proiect în cazul meu în care, de exemplu, aveți un id, și un nume și aș prefera să mențin starea pentru un proiect imbricate.

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

Anunțați-mă!

Eladian

Ceva de genul acesta ar putea fi suficient,

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) 

Elnoor

Știu că este o întrebare veche, dar totuși am vrut să împărtășesc cum am realizat acest lucru. Presupunând că starea în constructor arată astfel:

  constructor(props) {
    super(props);

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

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

My handleChange funcția mea este ca aceasta:

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

Și asigurați-vă că numiți intrările în mod corespunzător:

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

Emisael Carrera

Eu fac actualizări imbricate cu un reduce căutare:

Exemplu:

Variabilele imbricate din State:

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

Funcția:

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

Utilizați:

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

Ramiro Carbonell Delgado

Aceasta este starea mea inițială

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

Cârligul sau îl puteți înlocui cu starea (componentă de clasă)

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

Metoda pentru handleChange

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

Metoda pentru setarea stării cu stări imbricate

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

În cele din urmă, aceasta este intrarea pe care o folosesc

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

Sharad Sharma

Dacă folosiți formik în proiectul dvs., acesta are o modalitate ușoară de a gestiona aceste lucruri. Aici este cel mai simplu mod de a face cu formik.

Mai întâi setați valorile inițiale în atributul formik initivalues sau în react. state.

Aici, valorile inițiale sunt definite în react state

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

definiți valorile inițiale de mai sus pentru câmpul formik în interiorul formik initiValues attribute

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

Faceți o consolă pentru a verifica starea actualizată în string în loc de booleanformik setFieldValue pentru a seta starea sau folosiți instrumentul react debugger pentru a vedea modificările în afara valorilor de stare formik.

Soumya

încercați acest cod:

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

Comentarii

  • Dar acest lucru va elimina orice alte proprietăți prezente în obiectul la care se face referire prin someProperty și după ce codul de mai sus este executat doar flag proprietatea va rămâne –  > Por Yashas.
dhruv patel

puteți face acest lucru cu răspândirea obiectuluicod :

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

acest lucru va funcționa pentru mai multe proprietăți imbricate

shavina

Utilizați acest lucru pentru controlul de intrare multiplă și numele dinamic imbricate

<input type="text" name="title" placeholder="add title" onChange={this.handleInputChange} />
<input type="checkbox" name="chkusein" onChange={this.handleInputChange} />
<textarea name="body" id="" cols="30" rows="10" placeholder="add blog content" onChange={this.handleInputChange}></textarea>

codul foarte ușor de citit

gestionarul

handleInputChange = (event) => {
        const target = event.target;
        const value = target.type === 'checkbox' ? target.checked : target.value;
        const name = target.name;
        const newState = { ...this.state.someProperty, [name]: value }
        this.setState({ someProperty: newState })
    }

And_Web_App

Există o altă opțiune și aceasta funcționează dacă există mai multe elemente în lista de obiecte: copiați obiectul folosind this.state.Obj într-o variabilă (să zicem temp), utilizați metoda filter() pentru a traversa obiectul și a lua elementul particular pe care doriți să îl schimbați într-un obiect (numiți-l updateObj) și lista de obiecte rămase într-un alt obiect (numiți acest lucru ca restObj). Acum editați conținutul obiectului pe care doriți să îl actualizați, creând un nou element (de exemplu, newItem). Apoi, apelați this.setUpdate() și utilizați operatorii de răspândire pentru a atribui noua listă de obiecte la obiectul părinte.

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


var temp=[...this.state.someProperty]
var restObj = temp.filter((item) => item.flag !== true);
var updateObj = temp.filter((item) => item.flag === true);

var newItem = {
  flag: false
};
this.setState({ someProperty: [...restObj, newItem] });

Dewy Eve

am văzut următoarele într-o carte:

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

dar nu sunt sigur că este corect…