ฉันกำลังพยายามจัดระเบียบสถานะของฉันโดยใช้คุณสมบัติที่ซ้อนกันเช่นนี้:
this.state = {
someProperty: {
flag:true
}
}
แต่การอัพเดทสถานะเช่นนี้
this.setState({ someProperty.flag: false });
ไม่ทำงาน สิ่งนี้สามารถทำได้อย่างถูกต้อง?
ฉันกำลังพยายามจัดระเบียบสถานะของฉันโดยใช้คุณสมบัติที่ซ้อนกันเช่นนี้:
this.state = {
someProperty: {
flag:true
}
}
แต่การอัพเดทสถานะเช่นนี้
this.setState({ someProperty.flag: false });
ไม่ทำงาน สิ่งนี้สามารถทำได้อย่างถูกต้อง?
คำตอบ:
เพื่อsetState
วัตถุที่ซ้อนกันคุณสามารถทำตามวิธีการด้านล่างเพราะฉันคิดว่า setState ไม่รองรับการอัพเดทที่ซ้อนกัน
var someProperty = {...this.state.someProperty}
someProperty.flag = true;
this.setState({someProperty})
แนวคิดคือการสร้างวัตถุจำลองดำเนินการกับมันแล้วแทนที่สถานะขององค์ประกอบด้วยวัตถุที่อัปเดต
ตอนนี้ผู้ประกอบการแพร่กระจายสร้างสำเนาของวัตถุที่ซ้อนกันเพียงระดับเดียว หากรัฐของคุณอยู่ในระดับสูงเช่น:
this.state = {
someProperty: {
someOtherProperty: {
anotherProperty: {
flag: true
}
..
}
...
}
...
}
คุณสามารถ setState โดยใช้ตัวดำเนินการสเปรดในแต่ละระดับเช่น
this.setState(prevState => ({
...prevState,
someProperty: {
...prevState.someProperty,
someOtherProperty: {
...prevState.someProperty.someOtherProperty,
anotherProperty: {
...prevState.someProperty.someOtherProperty.anotherProperty,
flag: false
}
}
}
}))
อย่างไรก็ตามไวยากรณ์ข้างต้นได้รับทุกสิ่งที่น่าเกลียดเป็นรัฐที่ซ้อนกันมากขึ้นและด้วยเหตุนี้ฉันขอแนะนำให้คุณใช้ immutability-helper
แพคเกจเพื่อปรับปรุงสถานะ
immutability helper
ดูคำตอบนี้เกี่ยวกับวิธีการอัปเดตของรัฐที่มี
someProperty
และเรากำลังแก้ไขสิ่งนั้น
this.setState((prevState) => ({nested: {...prevState.nested, propertyToSet: newValue}})
น่าเบื่อนิดหน่อยแม้ว่า ...
...prevState,
ตัวอย่างสุดท้ายคุณsomeProperty
และลูก ๆ ของมัน
เพื่อเขียนมันในหนึ่งบรรทัด
this.setState({ someProperty: { ...this.state.someProperty, flag: false} });
this.state
ทันทีsetState
และคาดหวังว่ามันจะมีคุณค่าใหม่ เมื่อเทียบกับการโทรภายในthis.state
setState
หรือยังมีปัญหากับหลังที่ไม่ชัดเจนสำหรับฉัน
this.state
หลังจากsetState
นั้น นอกจากนี้เรายังไม่ได้ผ่านการthis.state
คุณสมบัติผู้ประกอบการแพร่กระจาย มันเหมือนกับ Object.assign () github.com/tc39/proposal-object-rest-spreadsetState
…
this.setState(currentState => { someProperty: { ...currentState.someProperty, flag: false} });
การหลีกเลี่ยงปัญหานี้ได้หรือไม่
บางครั้งคำตอบโดยตรงไม่ใช่คำตอบที่ดีที่สุด :)
เวอร์ชั่นสั้น:
รหัสนี้
this.state = {
someProperty: {
flag: true
}
}
ควรทำให้ง่ายขึ้นเป็นสิ่งที่ต้องการ
this.state = {
somePropertyFlag: true
}
รุ่นยาว:
ขณะนี้คุณไม่ต้องการทำงานกับสถานะซ้อนใน Reactที่คุณไม่ควรต้องการที่จะทำงานร่วมกับรัฐซ้อนกันในการตอบสนองเนื่องจาก React ไม่ได้มุ่งเน้นที่จะทำงานกับรัฐที่ซ้อนกันและโซลูชันทั้งหมดที่เสนอที่นี่จะดูเป็นแฮ็ก พวกเขาไม่ได้ใช้กรอบงาน แต่สู้กับมัน พวกเขาแนะนำให้เขียนรหัสไม่ชัดเจนเพื่อจุดประสงค์ในการจัดกลุ่มคุณสมบัติบางอย่าง ดังนั้นพวกเขาจึงน่าสนใจมากเป็นคำตอบสำหรับความท้าทาย แต่ไม่มีประโยชน์จริง ๆ
ให้จินตนาการถึงสถานะต่อไปนี้:
{
parent: {
child1: 'value 1',
child2: 'value 2',
...
child100: 'value 100'
}
}
จะเกิดอะไรขึ้นถ้าคุณเปลี่ยนเพียงค่าของchild1
? ปฏิกิริยาจะไม่ทำให้การแสดงผลอีกครั้งเพราะใช้การเปรียบเทียบแบบตื้นและจะพบว่าparent
คุณสมบัตินั้นไม่เปลี่ยนแปลง BTW กลายพันธุ์วัตถุของรัฐโดยตรงถือว่าเป็นการปฏิบัติที่ไม่ดีโดยทั่วไป
ดังนั้นคุณต้องสร้างใหม่ทั้งหมด parent
วัตถุใหม่แต่ในกรณีนี้เราจะพบปัญหาอื่น ปฏิกิริยาจะคิดว่าเด็กทุกคนมีการเปลี่ยนแปลงค่าของพวกเขาและจะทำให้พวกเขาทั้งหมดอีกครั้ง แน่นอนมันไม่ดีต่อประสิทธิภาพ
มันยังคงเป็นไปได้ที่จะแก้ปัญหาโดยการเขียนตรรกะที่ซับซ้อนบางอย่างshouldComponentUpdate()
แต่ฉันอยากจะหยุดที่นี่และใช้วิธีแก้ปัญหาง่าย ๆ จากเวอร์ชั่นสั้น ๆ
รัฐซ้อนใน React เป็นการออกแบบที่ผิด
อ่านคำตอบที่ยอดเยี่ยมนี้
การให้เหตุผลเบื้องหลังคำตอบนี้:
setState ของ React เป็นเพียงความสะดวกในตัว แต่ในไม่ช้าคุณก็จะรู้ว่ามันมีขีด จำกัด การใช้คุณสมบัติที่กำหนดเองและการใช้ forceUpdate อย่างชาญฉลาดจะช่วยให้คุณได้มากขึ้น เช่น:
class MyClass extends React.Component { myState = someObject inputValue = 42 ...
ตัวอย่างเช่น MobX, คูบอกสถานะอย่างสมบูรณ์และใช้คุณสมบัติที่สังเกตได้ที่กำหนดเอง
ใช้ Observables แทนสถานะใน React components
มีอีกวิธีที่สั้นกว่าในการอัปเดตคุณสมบัติที่ซ้อนอยู่
this.setState(state => {
state.nested.flag = false
state.another.deep.prop = true
return state
})
this.setState(state => (state.nested.flag = false, state))
หมายเหตุ: นี่คือการประกอบจุลภาค ~ MDNเห็นมันในการดำเนินการที่นี่ (Sandbox)
มันคล้ายกับ(แม้ว่าสิ่งนี้จะไม่เปลี่ยนการอ้างอิงสถานะ)
this.state.nested.flag = false
this.forceUpdate()
สำหรับความแตกต่างที่ลึกซึ้งในบริบทนี้ระหว่างforceUpdate
และsetState
ดูตัวอย่างที่เชื่อมโยง
แน่นอนว่านี่เป็นการละเมิดหลักการสำคัญบางประการอย่างที่state
ควรจะเป็นแบบอ่านอย่างเดียว แต่เนื่องจากคุณทิ้งสถานะเก่าและแทนที่มันด้วยสถานะใหม่ทันทีมันก็โอเคอย่างสมบูรณ์
แม้ว่าส่วนประกอบที่มีสถานะจะอัปเดตและแสดงซ้ำอย่างถูกต้อง( ยกเว้น gotcha นี้ )อุปกรณ์ประกอบฉากจะล้มเหลวในการเผยแพร่ต่อเด็ก ๆ(ดูความคิดเห็นของ Spymaster ด้านล่าง)ด้านล่าง) ใช้เทคนิคนี้เฉพาะเมื่อคุณรู้ว่าคุณกำลังทำอะไรอยู่
ตัวอย่างเช่นคุณอาจส่งเสาแบนที่มีการเปลี่ยนแปลงซึ่งอัปเดตและผ่านได้อย่างง่ายดาย
render(
//some complex render with your nested state
<ChildComponent complexNestedProp={this.state.nested} pleaseRerender={Math.random()}/>
)
ในขณะนี้แม้ว่าการอ้างอิงสำหรับ complexNestedProp จะไม่เปลี่ยนแปลง ( shouldComponentUpdate )
this.props.complexNestedProp === nextProps.complexNestedProp
คอมโพเนนต์จะแสดงผลใหม่เมื่อใดก็ตามที่มีการอัปเดตองค์ประกอบหลักซึ่งเป็นกรณีหลังจากการโทรthis.setState
หรือthis.forceUpdate
ในพาเรนต์
ใช้รัฐซ้อนกันและกรรมวิธีรัฐโดยตรงเป็นอันตรายเพราะวัตถุที่แตกต่างกันอาจถือ (โดยเจตนาหรือไม่) ที่แตกต่างกัน (เก่า) อ้างอิงถึงรัฐและอาจไม่จำเป็นต้องทราบเมื่อมีการอัปเดต (ตัวอย่างเช่นเมื่อใช้PureComponent
หรือถ้าshouldComponentUpdate
มีการดำเนินการเพื่อให้ผลตอบแทนfalse
) หรือมี มีวัตถุประสงค์เพื่อแสดงข้อมูลเก่าเช่นในตัวอย่างด้านล่าง
ลองนึกภาพเส้นเวลาที่ควรแสดงข้อมูลประวัติการกลายพันธุ์ข้อมูลภายใต้มือจะส่งผลให้เกิดพฤติกรรมที่ไม่คาดคิดเนื่องจากจะเปลี่ยนรายการก่อนหน้า
อย่างไรก็ตามที่นี่คุณจะเห็นNested PureChildClass
ว่าไม่ได้รับการจัดแสดงใหม่เนื่องจากอุปกรณ์ประกอบฉากล้มเหลวในการเผยแพร่
state
ทั้งหมดและใช้คุณสมบัติที่สังเกตได้ที่กำหนดเอง React's setState
เป็นเพียงความสะดวกสบายในตัว แต่ในไม่ช้าคุณจะรู้ว่ามันมีขีด จำกัด การใช้คุณสมบัติที่กำหนดเองและการใช้อย่างชาญฉลาดforceUpdate
ช่วยให้คุณได้มากขึ้น ใช้ Observables แทนสถานะใน React components
this.forceUpdate()
shouldComponentUpdate
หากคุณกำลังใช้ ES2015 คุณสามารถเข้าถึง Object.assign คุณสามารถใช้มันเพื่อปรับปรุงวัตถุที่ซ้อนกัน
this.setState({
someProperty: Object.assign({}, this.state.someProperty, {flag: false})
});
คุณผสานคุณสมบัติที่มีการปรับปรุงด้วยที่มีอยู่และใช้วัตถุที่ส่งคืนเพื่อปรับปรุงสถานะ
แก้ไข: เพิ่มวัตถุว่างเปล่าเป็นเป้าหมายในฟังก์ชั่นกำหนดเพื่อให้แน่ใจว่าสถานะไม่ได้กลายพันธุ์โดยตรงในขณะที่รถขุดชี้ให้เห็น
const newState = Object.assign({}, this.state);
newState.property.nestedProperty = "new value";
this.setState(newState);
property
คุณสมบัติมีหลายคุณสมบัติซ้อนกันอันดับแรกจะลบออกส่วนที่สองจะไม่ลบออก codesandbox.io/s/nameless-pine-42thu
มีห้องสมุดมากมายที่จะช่วยในเรื่องนี้ ตัวอย่างเช่นการใช้immutability-helper :
import update from 'immutability-helper';
const newState = update(this.state, {
someProperty: {flag: {$set: false}},
};
this.setState(newState);
ใช้ชุด lodash / fp :
import {set} from 'lodash/fp';
const newState = set(["someProperty", "flag"], false, this.state);
ใช้การผสาน lodash / fp :
import {merge} from 'lodash/fp';
const newState = merge(this.state, {
someProperty: {flag: false},
});
lodash
คุณอาจต้องการลอง_.cloneDeep
และตั้งค่าสถานะเป็นสำเนาที่ลอกแบบมา
lodash/fp
ดังนั้นเราไม่ต้องกังวลเกี่ยวกับการกลายพันธุ์
merge
หรือset
ฟังก์ชั่นใน lodash / fp นอกเหนือจากไวยากรณ์แล้ว?
this.setState(newState)
หาวิธีการ lodash / fp เช่นกัน gotcha อีกอย่างหนึ่งคือ lodash .set
(ไม่ใช่ fp) มีลำดับของการขัดแย้งที่แตกต่างกันซึ่งทำให้ฉันสะดุดในขณะที่
เราใช้ Immer https://github.com/mweststrate/immerเพื่อจัดการปัญหาประเภทนี้
เพียงแค่แทนที่รหัสนี้ในหนึ่งในองค์ประกอบของเรา
this.setState(prevState => ({
...prevState,
preferences: {
...prevState.preferences,
[key]: newValue
}
}));
ด้วยสิ่งนี้
import produce from 'immer';
this.setState(produce(draft => {
draft.preferences[key] = newValue;
}));
ด้วย immer คุณจัดการสถานะของคุณเป็น "วัตถุปกติ" ความมหัศจรรย์เกิดขึ้นเบื้องหลังด้วยวัตถุพร็อกซี
ต่อไปนี้เป็นรูปแบบของคำตอบแรกที่ให้ไว้ในชุดข้อความนี้ซึ่งไม่ต้องการแพ็คเกจเพิ่มเติมไลบรารีหรือฟังก์ชั่นพิเศษ
state = {
someProperty: {
flag: 'string'
}
}
handleChange = (value) => {
const newState = {...this.state.someProperty, flag: value}
this.setState({ someProperty: newState })
}
ในการตั้งค่าสถานะของเขตข้อมูลที่ซ้อนเฉพาะคุณได้ตั้งค่าวัตถุทั้งหมด ฉันไม่นี้โดยการสร้างตัวแปรnewState
และการแพร่กระจายเนื้อหาของรัฐในปัจจุบันเป็นมันเป็นครั้งแรกโดยใช้ ES2015 ประกอบการแพร่กระจาย จากนั้นฉันแทนที่ค่าของthis.state.flag
ด้วยค่าใหม่ (ตั้งแต่ฉันตั้งค่าflag: value
หลังจากที่ฉันกระจายสถานะปัจจุบันไปยังวัตถุflag
เขตข้อมูลในสถานะปัจจุบันจะถูกแทนที่) จากนั้นฉันก็กำหนดสถานะของวัตถุsomeProperty
ของฉันnewState
แม้ว่าการทำรังไม่ใช่วิธีการที่คุณควรปฏิบัติต่อสถานะส่วนประกอบบางครั้งอาจเป็นเรื่องง่ายสำหรับการทำรังแบบชั้นเดียว
สำหรับสถานะเช่นนี้
state = {
contact: {
phone: '888-888-8888',
email: 'test@test.com'
}
address: {
street:''
},
occupation: {
}
}
วิธีที่ใช้ซ้ำ ive ที่ใช้จะมีลักษณะเช่นนี้
handleChange = (obj) => e => {
let x = this.state[obj];
x[e.target.name] = e.target.value;
this.setState({ [obj]: x });
};
จากนั้นเพียงส่งผ่านชื่อ obj สำหรับการซ้อนแต่ละครั้งที่คุณต้องการระบุ ...
<TextField
name="street"
onChange={handleChange('address')}
/>
ฉันใช้วิธีนี้
หากคุณมีสถานะซ้อนอยู่เช่นนี้
this.state = {
formInputs:{
friendName:{
value:'',
isValid:false,
errorMsg:''
},
friendEmail:{
value:'',
isValid:false,
errorMsg:''
}
}
คุณสามารถประกาศฟังก์ชัน handleChange ที่คัดลอกสถานะปัจจุบันและกำหนดใหม่ด้วยค่าที่เปลี่ยนแปลง
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);
}
นี่คือ html พร้อมฟังเหตุการณ์
<input type="text" onChange={this.handleChange} " name="friendName" />
สร้างสำเนาของรัฐ:
let someProperty = JSON.parse(JSON.stringify(this.state.someProperty))
ทำการเปลี่ยนแปลงในวัตถุนี้:
someProperty.flag = "false"
ตอนนี้อัพเดตสถานะ
this.setState({someProperty})
แม้ว่าคุณจะถามเกี่ยวกับสถานะขององค์ประกอบ React แบบคลาส แต่ปัญหาเดียวกันนี้มีอยู่กับ useState hook ยิ่งแย่กว่านั้น: useState hook ไม่ยอมรับการอัพเดทบางส่วน ดังนั้นคำถามนี้จึงมีความเกี่ยวข้องมากเมื่อใช้ขอแนะนำสถานะตะขอ
ฉันได้ตัดสินใจที่จะโพสต์คำตอบต่อไปนี้เพื่อให้แน่ใจว่าคำถามครอบคลุมสถานการณ์ที่ทันสมัยกว่าที่ใช้ฮุก useState:
หากคุณมี:
const [state, setState] = useState({ someProperty: { flag: true, otherNestedProp: 1 }, otherProp: 2 })
คุณสามารถตั้งค่าคุณสมบัติที่ซ้อนกันได้โดยการโคลนปัจจุบันและแก้ไขส่วนที่ต้องการของข้อมูลตัวอย่างเช่น
setState(current => { ...current, someProperty: { ...current.someProperty, flag: false } });
หรือคุณสามารถใช้ไลบรารี Immer เพื่อทำให้การโคลนและการแพตช์ของวัตถุง่ายขึ้น
หรือคุณสามารถใช้ห้องสมุด Hookstate (ข้อจำกัดความรับผิดชอบ: ฉันเป็นผู้เขียน) เพียงแค่จัดการข้อมูลสถานะที่ซับซ้อน (ในระดับท้องถิ่นและระดับโลก) ทั้งหมดและปรับปรุงประสิทธิภาพ (อ่าน: ไม่ต้องกังวลเกี่ยวกับการเพิ่มประสิทธิภาพการแสดงผล):
import { useStateLink } from '@hookstate/core'
const state = useStateLink({ someProperty: { flag: true, otherNestedProp: 1 }, otherProp: 2 })
รับฟิลด์เพื่อแสดงผล:
state.nested.someProperty.nested.flag.get()
// or
state.get().someProperty.flag
ตั้งค่าฟิลด์ที่ซ้อนกัน:
state.nested.someProperty.nested.flag.set(false)
นี่คือตัวอย่างที่ Hookstate ที่รัฐลึก / ซ้อนซ้ำในลักษณะเหมือนต้นไม้โครงสร้างข้อมูล
อีกสองตัวเลือกที่ยังไม่ได้กล่าวถึง:
เพื่อทำสิ่งต่าง ๆ โดยทั่วไปฉันทำงานกับคำตอบของ @ ShubhamKhatri และ @ Qwerty
วัตถุของรัฐ
this.state = {
name: '',
grandParent: {
parent1: {
child: ''
},
parent2: {
child: ''
}
}
};
การควบคุมการป้อนข้อมูล
<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"
/>
เมธอด updateState
setState เป็นคำตอบของ @ 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 เป็นคำตอบของ @ 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);
}
หมายเหตุ: วิธีการข้างต้นไม่สามารถใช้กับอาร์เรย์ได้
ฉันให้ความสำคัญอย่างยิ่งกับข้อกังวลที่เปล่งออกมาโดยรอบแล้วในการสร้างสำเนาส่วนประกอบสถานะของคุณอย่างสมบูรณ์ กับที่กล่าวว่าฉันจะขอแนะนำให้อิมเมอ
import produce from 'immer';
<Input
value={this.state.form.username}
onChange={e => produce(this.state, s => { s.form.username = e.target.value }) } />
สิ่งนี้ควรใช้งานได้React.PureComponent
(เช่นการเปรียบเทียบสถานะที่ตื้นด้วย React) เนื่องจากImmer
ใช้พร็อกซีออบเจ็กต์อย่างชาญฉลาดเพื่อคัดลอกแผนผังของรัฐที่มีความลึกโดยพลการ อิมเมอร์ยังเป็นประเภทที่ปลอดภัยกว่าเมื่อเปรียบเทียบกับห้องสมุดอย่าง Immutability Helper และเหมาะสำหรับผู้ใช้จาวาสคริปต์และผู้ใช้ตัวเรียงพิมพ์
ฟังก์ชันอรรถประโยชน์ของtypescript
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)
}
obj
เป็นเพียงการอ้างอิงถึงรัฐดังนั้นคุณจึงกลายพันธุ์this.state
วัตถุจริง
ฉันพบสิ่งนี้จะทำงานสำหรับฉันมีแบบฟอร์มโครงการในกรณีของฉันที่เช่นคุณมี ID และชื่อและฉันอยากจะรักษาสถานะของโครงการที่ซ้อนกัน
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>
)
แจ้งให้เราทราบ!
บางสิ่งเช่นนี้อาจพอเพียง
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)
ฉันรู้ว่ามันเป็นคำถามเก่า แต่ก็ยังต้องการแบ่งปันวิธีที่ฉันประสบความสำเร็จ สมมติว่าสถานะในนวกรรมิกดูเหมือนว่านี้:
constructor(props) {
super(props);
this.state = {
loading: false,
user: {
email: ""
},
organization: {
name: ""
}
};
this.handleChange = this.handleChange.bind(this);
}
handleChange
ฟังก์ชั่นของฉันเป็นแบบนี้:
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]]};
});
}
และให้แน่ใจว่าคุณตั้งชื่ออินพุตตาม:
<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"
/>
ฉันทำการอัปเดตแบบซ้อนด้วยการลดการค้นหา:
ตัวอย่าง:
ตัวแปรที่ซ้อนกันในสถานะ:
state = {
coords: {
x: 0,
y: 0,
z: 0
}
}
ฟังก์ชั่น:
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})
}
ใช้:
<input
value={this.state.coords.x}
onChange={this.handleTextChange('coords.x')}
/>
นี่คือ initialState ของฉัน
const initialStateInput = {
cabeceraFamilia: {
familia: '',
direccion: '',
telefonos: '',
email: ''
},
motivoConsulta: '',
fechaHora: '',
corresponsables: [],
}
เบ็ดหรือคุณสามารถแทนที่ด้วยรัฐ (องค์ประกอบชั้น)
const [infoAgendamiento, setInfoAgendamiento] = useState(initialStateInput);
วิธีการสำหรับ handleChange
const actualizarState = e => {
const nameObjects = e.target.name.split('.');
const newState = setStateNested(infoAgendamiento, nameObjects, e.target.value);
setInfoAgendamiento({...newState});
};
วิธีการตั้งค่าสถานะด้วยสถานะซ้อนกัน
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;
}
ในที่สุดนี่คืออินพุตที่ฉันใช้
<input type="text" className="form-control" name="cabeceraFamilia.direccion" placeholder="Dirección" defaultValue={infoAgendamiento.cabeceraFamilia.direccion} onChange={actualizarState} />
หากคุณใช้ formik ในโครงการของคุณมีวิธีง่าย ๆ ในการจัดการสิ่งนี้ นี่เป็นวิธีที่ง่ายที่สุดในการทำ formik
ก่อนอื่นให้ตั้งค่าเริ่มต้นของคุณภายในแอตทริบิวต์ formik initivalues หรือในการตอบสนอง สถานะ
ที่นี่ค่าเริ่มต้นจะถูกกำหนดในสถานะตอบสนอง
state = {
data: {
fy: {
active: "N"
}
}
}
กำหนดข้างต้นค่าเริ่มต้นสำหรับเขตข้อมูล formik ภายในinitiValues
แอตทริบิวต์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>
สร้างคอนโซลเพื่อตรวจสอบสถานะที่อัพเดตเป็นstring
แทนฟังก์ชันboolean
formik setFieldValue
เพื่อตั้งค่าสถานะหรือใช้เครื่องมือดีบักเกอร์ตอบโต้เพื่อดูการเปลี่ยนแปลงที่เกิดขึ้นภายในค่าสถานะ formik
ลองรหัสนี้:
this.setState({ someProperty: {flag: false} });
someProperty
และหลังจากที่โค้ดข้างต้นถูกเรียกใช้งานflag
คุณสมบัติจะยังคงอยู่เท่านั้น
ฉันเห็นสิ่งต่อไปนี้ในหนังสือ:
this.setState(state => state.someProperty.falg = false);
แต่ฉันไม่แน่ใจว่ามันถูกต้อง ..