cbd*_*per 7 javascript json reactjs
我一直在考虑使用React setState()方法更新这些嵌套属性的最佳方法是什么。我也愿意考虑更高效的方法来考虑性能,并避免与其他可能的并发状态更改发生冲突。
注意:我使用的是extends的类组件React.Component。如果您使用React.PureComponent,则在更新嵌套属性时必须格外小心,因为如果您不更改的任何顶级属性,这可能不会触发重新渲染state。这是说明此问题的沙箱:
CodeSandbox-组件vs PureComponent和嵌套状态更改
回到这个问题-我在这里关注的是性能以及setState()在状态上更新嵌套属性时其他并发调用之间可能存在的冲突:
例:
假设我正在构建一个表单组件,并且将使用以下对象初始化表单状态:
this.state = {
isSubmitting: false,
inputs: {
username: {
touched: false,
dirty: false,
valid: false,
invalid: false,
value: 'some_initial_value'
},
email: {
touched: false,
dirty: false,
valid: false,
invalid: false,
value: 'some_initial_value'
}
}
}
Run Code Online (Sandbox Code Playgroud)
根据我的研究,通过使用setState(),React将浅化合并传递给它的对象,这意味着它仅将检查顶级属性,在此示例中为isSubmittingand inputs。
因此,我们可以将包含这两个顶级属性(isSubmitting和inputs)的完整newState对象传递给它,也可以传递这些属性之一,并且这些属性将被浅层合并为先前的状态。
问题1
您是否同意最好的做法是仅传递state我们正在更新的顶级属性?例如,如果我们不更新该isSubmitting属性,则应避免将其传递给setState()其他属性,以避免与可能同时与该属性setState()一起排队的其他并发调用发生冲突/覆盖。它是否正确?
在此示例中,我们将仅传递具有inputs属性的对象。这样可以避免与其他setState()可能试图更新该isSubmitting属性的冲突/覆盖。
问题2
从性能角度来看,复制当前状态以更改其嵌套属性的最佳方法是什么?
在这种情况下,请想象我要设置state.inputs.username.touched = true。
即使您可以这样做:
this.setState( (state) => {
state.inputs.username.touched = true;
return state;
});
Run Code Online (Sandbox Code Playgroud)
你不应该 因为,从React Docs中,我们有:
状态是在应用更改时对组件状态的引用。它不应该直接突变。相反,更改应通过根据状态和道具的输入来构建新对象来表示。
因此,从上面的摘录中我们可以推断出,我们应该从当前state对象中构建一个新对象,以便对其进行更改并根据需要对其进行操作,并传递给它setState()以更新state。
并且由于我们正在处理嵌套对象,因此我们需要一种深度复制对象的方法,并假设您不想使用任何第三方库(lodash)来这样做,那么我想出的是:
this.setState( (state) => {
let newState = JSON.parse(JSON.stringify(state));
newState.inputs.username.touched = true;
return ({
inputs: newState.inputs
});
});
Run Code Online (Sandbox Code Playgroud)
请注意,当您state嵌套了对象时,也不应使用let newState = Object.assign({},state)。因为那样会浅复制state嵌套对象的引用,因此您仍然可以直接更改状态,因为newState.inputs === state.inputs === this.state.inputs这是正确的。他们都指向同一个对象inputs。
但是,由于JSON.parse(JSON.stringify(obj))存在性能限制,并且还存在一些可能不支持JSON的数据类型或循环数据,因此,您建议采用什么其他方法深度复制嵌套对象以进行更新?
我想出的另一个解决方案是:
this.setState( (state) => {
let usernameInput = {};
usernameInput['username'] = Object.assign({},state.inputs.username);
usernameInput.username.touched = true;
let newInputs = Object.assign({},state.inputs,usernameInput);
return({
inputs: newInputs
});
};
Run Code Online (Sandbox Code Playgroud)
在第二种选择中,我要做的是从要更新的最里面的对象(在本例中为username对象)创建一个新对象。而且我必须在键中获取这些值username,这就是为什么我要使用它,usernameInput['username']因为以后我会将其合并到一个newInputs对象中。一切都使用完成Object.assign()。
第二种选择具有更好的性能结果。至少好50%。
关于这个主题还有其他想法吗?很长的问题很抱歉,但我认为它很好地说明了这个问题。
编辑:我从以下答案中采用的解决方案:
我的TextInput组件onChange事件侦听器(我通过React Context为它提供服务):
onChange={this.context.onChange(this.props.name)}
Run Code Online (Sandbox Code Playgroud)
我的表单组件中的onChange函数
onChange(inputName) {
return(
(event) => {
event.preventDefault();
const newValue = event.target.value;
this.setState( (prevState) => {
return({
inputs: {
...prevState.inputs,
[inputName]: {
...prevState.inputs[inputName],
value: newValue
}
}
});
});
}
);
}
Run Code Online (Sandbox Code Playgroud)
我可以想到其他一些方法来实现它。
Deconstructing every nested element and only overriding the right one :
this.setState(prevState => ({
inputs: {
...prevState.inputs,
username: {
...prevState.inputs.username,
touched: true
}
}
}))
Run Code Online (Sandbox Code Playgroud)
Using the deconstructing operator to copy your inputs :
this.setState(prevState => {
const inputs = {...prevState.inputs};
inputs.username.touched = true;
return { inputs }
})
Run Code Online (Sandbox Code Playgroud)
EDIT
First solution using computed properties :
this.setState(prevState => ({
inputs: {
...prevState.inputs,
[field]: {
...prevState.inputs.[field],
[action]: value
}
}
}))
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
3212 次 |
| 最近记录: |