javascript - React Hooks ใช้ useState() กับวัตถุ
วิธีที่ถูกต้องในการอัปเดต state ในอ็อบเจ็กต์ซ้อนกันใน React ด้วย Hooks คืออะไร?
export Example = () => {
const [exampleState, setExampleState] = useState(
{masterField: {
fieldOne: "a",
fieldTwo: {
fieldTwoOne: "b"
fieldTwoTwo: "c"
}
}
})
จะใช้ setExampleState เพื่ออัปเดต exampleState เป็น a (เพิ่มฟิลด์) อย่างไร?
const a = {
masterField: {
fieldOne: "a",
fieldTwo: {
fieldTwoOne: "b",
fieldTwoTwo: "c"
}
},
masterField2: {
fieldOne: "c",
fieldTwo: {
fieldTwoOne: "d",
fieldTwoTwo: "e"
}
},
}
}
b (กำลังเปลี่ยนค่า)?
const b = {masterField: {
fieldOne: "e",
fieldTwo: {
fieldTwoOne: "f"
fieldTwoTwo: "g"
}
}
})
คำตอบ #1
คุณสามารถส่งค่าใหม่ได้แบบนี้:
setExampleState({...exampleState, masterField2: {
fieldOne: "a",
fieldTwo: {
fieldTwoOne: "b",
fieldTwoTwo: "c"
}
},
})
คำตอบ #2
หากใครกำลังค้นหาการอัปเดต useState() hooks สำหรับ object
ผ่านการป้อนข้อมูล
const [state, setState] = useState({ fName: "", lName: "" });
const handleChange = e => {
const { name, value } = e.target;
setState(prevState => ({
...prevState,
[name]: value
}));
};
<input
value={state.fName}
type="text"
onChange={handleChange}
name="fName"
/>
<input
value={state.lName}
type="text"
onChange={handleChange}
name="lName"
/>
ผ่าน onSubmit หรือ การคลิกปุ่ม
setState(prevState => ({
...prevState,
fName: 'your updated value here'
}));
คำตอบ #3
โดยทั่วไปคุณควรระวังวัตถุที่ซ้อนลึกใน state ของ React เพื่อหลีกเลี่ยงพฤติกรรมที่ไม่คาดคิด state ควรถูกอัปเดตอย่างไม่เปลี่ยนแปลง เมื่อคุณมีวัตถุลึก คุณจะต้องทำการโคลนลึกเพื่อให้คงความไม่เปลี่ยนแปลง ซึ่งอาจมีค่าใช้จ่ายสูงใน React ทำไม?
เมื่อคุณทำการโคลนสถานะอย่างลึก React จะคำนวณใหม่และเรนเดอร์ทุกอย่างที่ขึ้นอยู่กับตัวแปรเหล่านั้น แม้ว่าตัวแปรเหล่านั้นยังไม่เปลี่ยนแปลงก็ตาม!
ดังนั้น ก่อนที่จะพยายามแก้ไขปัญหาของคุณ ลองคิดว่าคุณจะทำให้สถานะเรียบง่ายขึ้นได้อย่างไร เมื่อคุณทำเช่นนั้นแล้ว คุณจะพบเครื่องมือที่สะดวกที่จะช่วยจัดการกับสถานะขนาดใหญ่ เช่น useReducer()
ในกรณีที่คุณคิดเกี่ยวกับมัน แต่ยังมั่นใจว่าคุณจำเป็นต้องใช้โครงสร้างสถานะแบบซ้อนลึก คุณยังสามารถใช้ useState() ร่วมกับไลบรารีอย่าง immutable.js และ Immutability-helper ได้ พวกมันทำให้การอัปเดตหรือคัดลอกอ็อบเจ็กต์ลึกเป็นเรื่องง่ายโดยไม่ต้องกังวลเกี่ยวกับความสามารถในการเปลี่ยนแปลง
คำตอบ #4
ฉันมาสายงานปาร์ตี้.. :)
@aseferov คำตอบใช้ได้ผลดีมากเมื่อมีความตั้งใจที่จะป้อนโครงสร้างวัตถุทั้งหมดใหม่ อย่างไรก็ตาม หากเป้าหมาย/ความมุ่งหมายคือการอัปเดตค่าสำหรับฟิลด์เฉพาะในวัตถุ ผมเชื่อว่าวิธีด้านล่างนี้ดีกว่า
สถานการณ์:
const [infoData, setInfoData] = useState({
major: {
name: "John Doe",
age: "24",
sex: "M",
},
minor:{
id: 4,
collegeRegion: "south",
}
});
การอัปเดตระเบียนเฉพาะจะต้องมีการเรียกกลับไปยังสถานะก่อนหน้า prevState
ที่นี่ :
setInfoData((prevState) => ({
...prevState,
major: {
...prevState.major,
name: "Tan Long",
}
}));
บางที
setInfoData((prevState) => ({
...prevState,
major: {
...prevState.major,
name: "Tan Long",
},
minor: {
...prevState.minor,
collegeRegion: "northEast"
}));
ฉันหวังว่านี่จะช่วยใครก็ตามที่พยายามแก้ปัญหาที่คล้ายกันได้
คำตอบ #5
สามารถใช้ฮุค useReducer สำหรับการจัดการสถานะที่ซับซ้อน แทนการใช้ useState ในการทำเช่นนี้ ให้เริ่มต้นโดยการกำหนดสถานะและฟังก์ชันการอัปเดตดังนี้:
const initialState = { name: "Bob", occupation: "builder" };
const [state, updateState] = useReducer(
(state, updates) => ({ ...state, ...updates }),
initialState
);
แล้วคุณสามารถอัปเดตสถานะโดยส่งการอัปเดตบางส่วนเท่านั้น เช่นนี้:
updateState({ occupation: "postman" })
คำตอบ #6
ในปี 2022
หากคุณกำลังมองหาฟังก์ชันการทำงานเดียวกับ this.setState (มาจาก class components) ใน functional components นี่คือคำตอบที่จะช่วยคุณได้มาก
ตัวอย่างเช่น
คุณมี state แบบด้านล่างและต้องการอัปเดตเฉพาะฟิลด์ที่ระบุจากทั้ง state ดังนั้นคุณจำเป็นต้องใช้ object destructing ทุกครั้ง และบางครั้งมันอาจทำให้รำคาญได้
const [state, setState] = useState({first: 1, second: 2});
// results will be state = {first: 3} instead of {first: 3, second: 2}
setState({first: 3})
// To resolve that you need to use object destructing every time
// results will be state = {first: 3, second: 2}
setState(prev => ({...prev, first: 3}))
เพื่อแก้ปัญหานั้น ฉันจึงคิดวิธี useReducer กรุณาตรวจสอบ useReducer
const stateReducer = (state, action) => ({
...state,
...(typeof action === 'function' ? action(state) : action),
});
const [state, setState] = useReducer(stateReducer, {first: 1, second: 2});
// results will be state = {first: 3, second: 2}
setState({first: 3})
// you can also access the previous state callback if you want
// results will remain same, state = {first: 3, second: 2}
setState(prev => ({...prev, first: 3}))
คุณสามารถเก็บ stateReducer ไว้ในไฟล์ utils และนำเข้าในทุกไฟล์ถ้าคุณต้องการได้
นี่คือตะขอที่กําหนดเองหากคุณต้องการ
import React from 'react';
export const stateReducer = (state, action) => ({
...state,
...(typeof action === 'function' ? action(state) : action),
});
const useReducer = (initial, lazyInitializer = null) => {
const [state, setState] = React.useReducer(stateReducer, initial, init =>
lazyInitializer ? lazyInitializer(init) : init
);
return [state, setState];
};
export default useReducer;
พิมพ์ดีด
import React, { Dispatch } from "react";
type SetStateAction<S> = S | ((prev: S) => S);
type STATE<R> = [R, Dispatch<SetStateAction<Partial<R>>>];
const stateReducer = (state, action) => ({
...state,
...(typeof action === "function" ? action(state) : action),
});
const useReducer = <S>(initial, lazyInitializer = null): STATE<S> => {
const [state, setState] = React.useReducer(stateReducer, initial, (init) =>
lazyInitializer ? lazyInitializer(init) : init,
);
return [state, setState];
};
export default useReducer;
คำตอบ #7
ขอบคุณ ฟิลิป สิ่งนี้ช่วยฉันได้ - กรณีการใช้งานของฉันคือฉันมีฟอร์มที่มีช่องกรอกข้อมูลจำนวนมาก ดังนั้นฉันจึงเก็บสถานะเริ่มต้นเป็นอ็อบเจกต์ และฉันไม่สามารถอัปเดตสถานะอ็อบเจกต์ได้ โพสต์ด้านบนช่วยฉันได้ :)
const [projectGroupDetails, setProjectGroupDetails] = useState({
"projectGroupId": "",
"projectGroup": "DDD",
"project-id": "",
"appd-ui": "",
"appd-node": ""
});
const inputGroupChangeHandler = (event) => {
setProjectGroupDetails((prevState) => ({
...prevState,
[event.target.id]: event.target.value
}));
}
<Input
id="projectGroupId"
labelText="Project Group Id"
value={projectGroupDetails.projectGroupId}
onChange={inputGroupChangeHandler}
/>
