React将useState()与Object挂钩

在嵌套对象中,在React with Hooks中更新状态的正确方法是什么?

export Example = () => {
const [exampleState, setExampleState] = useState(
{masterField: {
fieldOne: "a",
fieldTwo: {
fieldTwoOne: "b"
fieldTwoTwo: "c"
}
}
})

如何使用setExampleStateexampleState更新为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"
}
}
})
497685 次浏览

你可以像这样传递新值:

  setExampleState({...exampleState,  masterField2: {
fieldOne: "a",
fieldTwo: {
fieldTwoOne: "b",
fieldTwoTwo: "c"
}
},
})

一般来说,你应该注意React状态下嵌套很深的对象。为了避免意外的行为,状态应该不可更改地更新。当你有深层对象时,你最终会为了不变性而对它们进行深层克隆,这在React中是相当昂贵的。为什么?

一旦你深度克隆状态,React将重新计算和重新渲染所有依赖于变量的东西,即使它们没有改变!

因此,在尝试解决问题之前,首先考虑如何将状态变平。一旦您这样做了,您就会发现有助于处理大型状态的方便工具,例如useReducer()。

如果你仔细考虑过,但仍然确信需要使用深度嵌套的状态树,你仍然可以将useState()与immutable.jsImmutability-helper这样的库一起使用。它们使得更新或克隆深层对象变得简单,而不必担心可变性。

我给您留下了一个实用函数来不可变地更新对象

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

我迟到了。:)

@aseferov回答在意图是重新进入整个对象结构时非常有效。然而,如果目标/目标是更新Object中的特定字段值,我认为下面的方法更好。

情境:

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"


}));

我希望这对试图解决类似问题的人有所帮助。

如果有人正在搜索useState ()钩子,请更新为< /强> <强>对象

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

谢谢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}
/>




function App() {


const [todos, setTodos] = useState([
{ id: 1, title: "Selectus aut autem", completed: false },
{ id: 2, title: "Luis ut nam facilis et officia qui", completed: false },
{ id: 3, title: "Fugiat veniam minus", completed: false },
{ id: 4, title: "Aet porro tempora", completed: true },
{ id: 5, title: "Laboriosam mollitia et enim quasi", completed: false }
]);


const changeInput = (e) => {todos.map(items => items.id === parseInt(e.target.value) && (items.completed = e.target.checked));
setTodos([...todos], todos);}
return (
<div className="container">
{todos.map(items => {
return (
<div key={items.id}>
<label>
<input type="checkbox"
onChange={changeInput}
value={items.id}
checked={items.completed} />&nbsp; {items.title}</label>
</div>
)
})}
</div>
);
}

最初我在useState中使用object,但后来我移动到useReducer钩子用于复杂的情况。重构代码时,我感到性能有所提高。

当您有涉及多个子值的复杂状态逻辑时,或者当下一个状态依赖于前一个状态时,useReducer通常比useState更可取。

useReducer React docs

我已经实现了这样的钩子供我自己使用:

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

更相关的钩子

我认为最好的解决方案是其次是音麦。它允许你像直接修改字段一样更新对象(masterField.fieldOne。Fieldx = 'abc')。但它当然不会改变实际对象。它收集草稿对象上的所有更新,并在最后为您提供一个最终对象,您可以使用它来替换原始对象。

,就像这个例子:

首先创建对象的状态:

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

你必须使用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
}));

我认为更优雅的解决方案是创建更新后的状态对象,同时保留以前的state值。需要更新的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>
)
}

我已经给出了两个追加,整个对象更新,具体的关键更新的解决方案的例子

追加和修改都可以通过一个简单的步骤来完成。我认为这是更稳定和安全的,没有不可变或可变的依赖。

这是如何追加新对象

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

你想要创建状态的对象

let teams = {
team: [
{
name: "one",
id: "1"
},
]
}

使团队的状态为对象

const [state, setState] = useState(teams);

像这样更新状态

setState((prevState)=>({...prevState,team:[
...prevState.team,
{
name: "two",
id: "2"
}
]}))

更新后状态变为

{
team: [
{
name: "one",
id: "1"
},
{
name: "two",
id: "2"
}
]
}

要渲染项目根据当前状态使用地图功能

{state.team.map((curr_team) => {
return (
<div>
<p>{curr_team.id}</p>
<p>{curr_team.name}</p>
</div>
)
})}

可以使用useReducer钩子来管理复杂的状态,而不是useState。要做到这一点,首先初始化状态和更新函数如下所示:

const initialState = { name: "Bob", occupation: "builder" };
const [state, updateState] = useReducer(
(state, updates) => ({ ...state, ...updates }),
initialState
);

然后你可以通过只传递部分更新来更新状态,就像这样:

updateState({ occupation: "postman" })

2022年

如果你在functional components中寻找与this.setState(来自class components)相同的功能,那么这个答案会对你有很大帮助。

例如

如果你有一个如下所示的状态,并且只想从整个状态中更新特定的字段,那么你需要每次都使用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文件中,如果你愿意,也可以将它导入到每个文件中。

这里是custom hook

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;

如果你使用布尔值和数组,这可以帮助你:

const [checkedOrders, setCheckedOrders] = useState<Record<string, TEntity>>({});


const handleToggleCheck = (entity: TEntity) => {
const _checkedOrders = { ...checkedOrders };
const isChecked = entity.id in checkedOrders;


if (isChecked) {
delete _checkedOrders[entity.id];
} else {
_checkedOrders[entity.id] = entity;
}


setCheckedOrders(_checkedOrders);
};

答案已经有了,但是这种类型没有被提到,所以看看这种类型的例子…

 const[data,setdata]= useState({
username: [
email,
"required",
//...some additional codes
],
password: [
password,
"required|password-5",
//..additional code if any..
],
})

**要在输入字段中更新状态变量email,您可以添加类似的代码与您的变量名**

          <Input
onChangeText={(t) => setdata(prevState=>({...prevState,username:{[0]:t}}))}
value={data.username[0]}
/>