Î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?
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
.
- @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 – > . - acest lucru nu a funcționat pentru mine… eu folosesc versiunea react @15.6.1 – > .
- @Stophface Putem folosi Lodash pentru a clona în profunzime, sigur, dar nu toată lumea ar include această bibliotecă doar pentru a setaState – > .
- 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… – > . - Nu cred că aveți nevoie de
...prevState,
în ultimul exemplu, ci doarsomeProperty
și copiii săi – > .
25
Pentru a scrie într-un singur rând
this.setState({ someProperty: { ...this.state.someProperty, flag: false} });
- 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 – > .
- 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 apelareathis.state
în cadrulsetState
. Sau există o problemă cu aceasta din urmă care nu îmi este clară? – > . - 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 trecemthis.state
lasetState
.…
a răspândit proprietăți. Este același lucru cu Object.assign(). github.com/tc39/proposal-object-rest-spread – > . - @RaghuTeja poate că doar făcând
this.setState(currentState => { someProperty: { ...currentState.someProperty, flag: false} });
ar putea permite evitarea acestei probleme? – > . - Am adăugat răspunsul cum să realizez același lucru cu cârligul useState pentru complezență. – > .
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ă.
- 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/… – > .
- 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. – > .
- 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. – > .
- @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. – > .
- 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. – > .
19
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ă a
forceUpdate
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ă.
- 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. – > .
- @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
. – > . - 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.) – > . - @MegaProger Acesta este un Operatorul de virgulă ~MDN. Și, de fapt, returnând doar un obiect gol
(exp1, {})
însetState
ar funcționa, de asemenea, dar nu am vrut să creez noi obiecte în mod inutil. sandbox – > . - 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. – > .
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.
- Și s-ar putea să mă înșel, dar nu cred că folosești React. fără ES2015. – > .
const newState = Object.assign({}, this.state);
newState.property.nestedProperty = "new value";
this.setState(newState);
- 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ă – > .
- 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 – > . - Qwerty, aveți dreptate, mulțumesc pentru efort. Am eliminat versiunea ES6. – > .
- Direct și simplu! – > .
- 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? – > .
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},
});
- Dacă utilizați
lodash
, probabil doriți să încercați_.cloneDeep
și să setați starea ca fiind copia clonată. – > . - @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. – > . - Vreun avantaj în a alege
merge
sauset
funcții în lodash/fp în afară de sintaxă? – > . - 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. – > . - Merge are un comportament ciudat, atunci când se apelează
this.setState(newState)
, creează un alt element de stare numitnewState
înthis.state
. Aveți vreo idee de ce? – > .
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.
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.
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')}
/>
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" />
- Confirmat. Acest lucru funcționează foarte bine dacă aveți de-a face cu un proiect deja stabilit cu proprietăți imbricate. – > .
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})
- 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. – > .
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.
Alte două opțiuni care nu au fost menționate încă:
- 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.
- 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.
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
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)}
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)
}
- 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 – > .
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ă.
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ă!
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)
Ș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"
/>
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')}
/>
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} />
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 boolean
formik setFieldValue
pentru a seta starea sau folosiți instrumentul react debugger pentru a vedea modificările în afara valorilor de stare formik.
încercați acest cod:
this.setState({ someProperty: {flag: false} });
- 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 doarflag
proprietatea va rămâne – > .
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
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 })
}
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] });
am văzut următoarele într-o carte:
this.setState(state => state.someProperty.falg = false);
dar nu sunt sigur că este corect…
20
101