React with 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"
}
}
})
答案 0 :(得分:54)
如果有人在搜索 useState(),则挂钩 object
的更新- Through Input
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"
/>
***************************
- Through onSubmit or button click
setState(prevState => ({
...prevState,
fName: 'your updated value here'
}));
答案 1 :(得分:12)
您可以像这样传递新值
setExampleState({...exampleState, masterField2: {
fieldOne: "c",
fieldTwo: {
fieldTwoOne: "d",
fieldTwoTwo: "e"
}
},
}})
答案 2 :(得分:7)
感谢Philip这对我有所帮助-我的用例是我有一个带有很多输入字段的表单,因此我将初始状态保持为对象,而无法更新对象状态。以上文章对我有帮助:)
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}
/>
答案 3 :(得分:6)
我迟到了派对..:)
当打算重新输入整个对象结构时,@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"
}));
我希望这对尝试解决类似问题的人有所帮助。
答案 4 :(得分:3)
Require Import String.
Open Scope string_scope.
Lemma L0 : "x" <> "y".
Proof.
intros H. inversion H.
Qed.
答案 5 :(得分:3)
您必须使用 Rest 参数和扩展语法 (https://javascript.info/rest-parameters-spread) 并设置一个以 preState 作为 setState 参数的函数。
不起作用(缺少功能)
[state, setState] = useState({})
const key = 'foo';
const value = 'bar';
setState({
...state,
[key]: value
});
确实有效!
[state, setState] = useState({})
const key = 'foo';
const value = 'bar';
setState(prevState => ({
...prevState,
[key]: value
}));
答案 6 :(得分:1)
通常,您应该注意处于React状态的深层嵌套对象。为避免意外行为,状态应不变地更新。当您有很深的对象时,您最终会出于不可变性而对其进行深克隆,这在React中可能会非常昂贵。为什么?
一旦您深度克隆了状态,React将重新计算并重新渲染依赖于变量的所有内容,即使它们没有改变!
因此,在尝试解决问题之前,请先考虑如何才能使状态变平坦。这样做之后,您会发现漂亮的工具可以帮助处理大型状态,例如useReducer()。
如果您考虑过这一点,但仍然确信需要使用深度嵌套的状态树,则仍可以将useState()与immutable.js和Immutability-helper之类的库一起使用。它们使更新或克隆深层对象变得简单,而不必担心可变性。
答案 7 :(得分:1)
我认为最好的解决方案是Immer。它允许您像直接修改字段一样更新对象(masterField.fieldOne.fieldx ='abc')。但这当然不会改变实际的对象。它会收集草稿对象上的所有更新,并在最后为您提供最终对象,您可以用它来替换原始对象。
答案 8 :(得分:1)
,像这个例子一样做:
对象的第一个创建状态:
const [isSelected, setSelection] = useState([{ id_1: false }, { id_2: false }, { id_3: false }]);
然后更改它们的值:
// if the id_1 is false make it true or return it false.
onValueChange={() => isSelected.id_1 == false ? setSelection([{ ...isSelected, id_1: true }]) : setSelection([{ ...isSelected, id_1: false }])}
答案 9 :(得分:1)
附加和修改都可以通过一个简单的步骤完成。我认为这是更稳定和安全的,没有一成不变或可变的依赖。
这是如何追加新对象
setExampleState(prevState => ({
...prevState,
masterField2: {
fieldOne: "c",
fieldTwo: {
fieldTwoOne: "d",
fieldTwoTwo: "e"
}
},
}))
假设您想再次修改 masterField2 对象。可能有两种情况。您想要更新整个对象或更新对象的特定键。
更新整个对象 - 所以这里键 masterField2 的整个值将被更新。 >
setExampleState(prevState => ({
...prevState,
masterField2: {
fieldOne: "c",
fieldTwo: {
fieldTwoOne: "d",
fieldTwoTwo: "e"
}
},
}))
但是,如果您只想更改 masterField2 对象内的 fieldTwoOne 键,该怎么办?您执行以下操作。
let oldMasterField2 = exampleState.masterField2
oldMasterField2.fieldTwo.fieldTwoOne = 'changed';
setExampleState(prevState => ({
...prevState,
masterField2: oldMasterField2
}))
答案 10 :(得分:0)
我留下了一个实用的函数来不变地更新对象
/**
* Inmutable update object
* @param {Object} oldObject Object to update
* @param {Object} updatedValues Object with new values
* @return {Object} New Object with updated values
*/
export const updateObject = (oldObject, updatedValues) => {
return {
...oldObject,
...updatedValues
};
};
所以您可以像这样使用它
const MyComponent = props => {
const [orderForm, setOrderForm] = useState({
specialities: {
elementType: "select",
elementConfig: {
options: [],
label: "Specialities"
},
touched: false
}
});
// I want to update the options list, to fill a select element
// ---------- Update with fetched elements ---------- //
const updateSpecialitiesData = data => {
// Inmutably update elementConfig object. i.e label field is not modified
const updatedOptions = updateObject(
orderForm[formElementKey]["elementConfig"],
{
options: data
}
);
// Inmutably update the relevant element.
const updatedFormElement = updateObject(orderForm[formElementKey], {
touched: true,
elementConfig: updatedOptions
});
// Inmutably update the relevant element in the state.
const orderFormUpdated = updateObject(orderForm, {
[formElementKey]: updatedFormElement
});
setOrderForm(orderFormUpdated);
};
useEffect(() => {
// some code to fetch data
updateSpecialitiesData.current("specialities",fetchedData);
}, [updateSpecialitiesData]);
// More component code
}
如果没有,您在这里有更多实用程序:https://es.reactjs.org/docs/update.html
答案 11 :(得分:0)
最初,我在useState中使用了对象,但是后来在复杂的情况下,我转向了useReducer挂钩。重构代码后,我感觉到了性能上的改善。
当具有复杂的状态逻辑(涉及多个子值)或下一个状态取决于前一个值时,useReducer通常比useState更可取。
我已经实现了此类挂钩供自己使用:
/**
* Same as useObjectState but uses useReducer instead of useState
* (better performance for complex cases)
* @param {*} PropsWithDefaultValues object with all needed props
* and their initial value
* @returns [state, setProp] state - the state object, setProp - dispatch
* changes one (given prop name & prop value) or multiple props (given an
* object { prop: value, ...}) in object state
*/
export function useObjectReducer(PropsWithDefaultValues) {
const [state, dispatch] = useReducer(reducer, PropsWithDefaultValues);
//newFieldsVal={[field_name]: [field_value], ...}
function reducer(state, newFieldsVal) {
return { ...state, ...newFieldsVal };
}
return [
state,
(newFieldsVal, newVal) => {
if (typeof newVal !== "undefined") {
const tmp = {};
tmp[newFieldsVal] = newVal;
dispatch(tmp);
} else {
dispatch(newFieldsVal);
}
},
];
}
更多相关的hooks。
答案 12 :(得分:0)
我认为一个更优雅的解决方案是创建更新的状态对象,同时保留之前的状态值。需要更新的 Object 属性可以像这样的数组形式提供 -
import React,{useState, useEffect} from 'react'
export default function Home2(props) {
const [x, setX] = useState({name : '',add : {full : '', pin : '', d : { v : '' }}})
const handleClick = (e, type)=>{
let obj = {}
if(type.length > 1){
var z = {}
var z2 = x[type[0]]
type.forEach((val, idx)=>{
if(idx === type.length - 1){
z[val] = e.target.value
}
else if(idx > 0){
Object.assign(z , z2) /*{...z2 , [val]:{} }*/
z[val] = {}
z = z[val]
z2 = z2[val]
}else{
z = {...z2}
obj = z
}
})
}else obj = e.target.value
setX( { ...x , [type[0]] : obj } )
}
return (
<div>
<input value = {x.name} onChange={e=>handleClick(e,["name"])}/>
<input value = {x.add.full} onChange={e=>handleClick(e,["add","full"])} />
<input value = {x.add.pin} onChange={e=>handleClick(e,["add","pin"])} /><br/>
<input value = {x.add.d.v} onChange={e=>handleClick(e,["add","d","v"])} /><br/>
{x.name} <br/>
{x.add.full} <br/>
{x.add.pin} <br/>
{x.add.d.v}
</div>
)
}