使用扩展运算符连续多次设置状态
Using spread operator to setState in multiple succession
我在本地存储中存储了一些值。当我的组件安装时,我想将这些值加载到状态中。但是,只会将最后添加的 属性 添加到状态中。我检查了 localStorage 上的值,它们都在那里。此外,当我在条件块中记录变量(desc、pic 或 foo)时,它们就在那里。
起初我以为每个后续 if 块都在重写状态,但事实并非如此,因为我正确使用了扩展运算符(我认为!),毕竟添加了新的 属性预先存在的属性。
我认为问题在于最后一个 if 块中的代码是 运行 在第一个 if 块中设置状态之前。我该如何编写代码,以便将所有三个属性从我的本地存储中获取到状态中?
//what I expect state to be
{
textArea: {
desc: 'some desc',
pic: 'some pic',
foo: 'some foo'
}
}
//what the state is
{
textArea: {
foo: 'some foo'
}
}
componentDidMount () {
const desc = window.localStorage.getItem('desc');
const pic = window.localStorage.getItem('pic');
const foo = window.localStorage.getItem('foo');
if (desc) {
console.log(desc) //'some desc'
this.setState({
...this.state,
textArea: {
...this.state.textArea,
desc: desc,
},
}, ()=>console.log(this.state.textArea.desc)); //undefined
}
if (pic) {
console.log(pic) //'some pic'
this.setState({
...this.state,
textArea: {
...this.state.textArea,
pic: pic,
},
}, ()=>console.log(this.state.textArea.pic)); //undefined
}
if (foo) {
console.log(foo) //'some foo'
this.setState({
...this.state,
textArea: {
...this.state.textArea,
foo: foo,
},
}, ()=>console.log(this.state.textArea.foo)); //'some foo'
}
}
你可能会被 React 批处理 setState 调用捕获,方法是浅合并你传递的参数。这将导致仅应用最后更新。您可以通过仅调用一次 setState 来解决此问题,例如:
componentDidMount () {
const desc = window.localStorage.getItem('desc');
const pic = window.localStorage.getItem('pic');
const foo = window.localStorage.getItem('foo');
this.setState({
textArea: Object.assign({},
desc ? { desc } : {},
pic ? { pic } : {},
foo ? { foo } : {}
)
});
}
另一个版本是将更新 函数 传递给 setState 而不是更新对象,这可以安全地用于多次调用。该函数传递了两个参数:先前的状态和当前的道具 - 无论您从函数中 return 将被设置为新状态。
componentDidMount () {
const desc = window.localStorage.getItem('desc');
const pic = window.localStorage.getItem('pic');
const foo = window.localStorage.getItem('foo');
this.setState(prevState => {
if (desc) {
return {
textArea: {
...prevState.textArea,
desc
}
}
} else {
return prevState;
}
});
// Repeat for other properties
}
使用这种方法有点冗长,但确实提供了在组件外部提取状态更新函数以实现可测试性的机会:
// Outside component
const updateSubProperty = (propertyName, spec) => prevState => {
return {
[propertyName]: {
...prevState[propertyName],
...spec
}
}
}
const filterNullProperties = obj => {
return Object.keys(obj).reduce((out, curr) => {
return obj[curr] ? { ...out, [curr]: obj[curr] } : out;
}, {});
}
componentDidMount () {
this.setState(updateSubProperty("textArea",
filterNullProperties(
desc: window.localStorage.getItem('desc'),
pic: window.localStorage.getItem('pic'),
foo: window.localStorage.getItem('foo')
)
));
}
这种方式增加了一些复杂性,但(在我看来)提供了一个真正可读的组件,让我们未来的自己清楚我们正在努力实现的目标。
我在本地存储中存储了一些值。当我的组件安装时,我想将这些值加载到状态中。但是,只会将最后添加的 属性 添加到状态中。我检查了 localStorage 上的值,它们都在那里。此外,当我在条件块中记录变量(desc、pic 或 foo)时,它们就在那里。
起初我以为每个后续 if 块都在重写状态,但事实并非如此,因为我正确使用了扩展运算符(我认为!),毕竟添加了新的 属性预先存在的属性。
我认为问题在于最后一个 if 块中的代码是 运行 在第一个 if 块中设置状态之前。我该如何编写代码,以便将所有三个属性从我的本地存储中获取到状态中?
//what I expect state to be
{
textArea: {
desc: 'some desc',
pic: 'some pic',
foo: 'some foo'
}
}
//what the state is
{
textArea: {
foo: 'some foo'
}
}
componentDidMount () {
const desc = window.localStorage.getItem('desc');
const pic = window.localStorage.getItem('pic');
const foo = window.localStorage.getItem('foo');
if (desc) {
console.log(desc) //'some desc'
this.setState({
...this.state,
textArea: {
...this.state.textArea,
desc: desc,
},
}, ()=>console.log(this.state.textArea.desc)); //undefined
}
if (pic) {
console.log(pic) //'some pic'
this.setState({
...this.state,
textArea: {
...this.state.textArea,
pic: pic,
},
}, ()=>console.log(this.state.textArea.pic)); //undefined
}
if (foo) {
console.log(foo) //'some foo'
this.setState({
...this.state,
textArea: {
...this.state.textArea,
foo: foo,
},
}, ()=>console.log(this.state.textArea.foo)); //'some foo'
}
}
你可能会被 React 批处理 setState 调用捕获,方法是浅合并你传递的参数。这将导致仅应用最后更新。您可以通过仅调用一次 setState 来解决此问题,例如:
componentDidMount () {
const desc = window.localStorage.getItem('desc');
const pic = window.localStorage.getItem('pic');
const foo = window.localStorage.getItem('foo');
this.setState({
textArea: Object.assign({},
desc ? { desc } : {},
pic ? { pic } : {},
foo ? { foo } : {}
)
});
}
另一个版本是将更新 函数 传递给 setState 而不是更新对象,这可以安全地用于多次调用。该函数传递了两个参数:先前的状态和当前的道具 - 无论您从函数中 return 将被设置为新状态。
componentDidMount () {
const desc = window.localStorage.getItem('desc');
const pic = window.localStorage.getItem('pic');
const foo = window.localStorage.getItem('foo');
this.setState(prevState => {
if (desc) {
return {
textArea: {
...prevState.textArea,
desc
}
}
} else {
return prevState;
}
});
// Repeat for other properties
}
使用这种方法有点冗长,但确实提供了在组件外部提取状态更新函数以实现可测试性的机会:
// Outside component
const updateSubProperty = (propertyName, spec) => prevState => {
return {
[propertyName]: {
...prevState[propertyName],
...spec
}
}
}
const filterNullProperties = obj => {
return Object.keys(obj).reduce((out, curr) => {
return obj[curr] ? { ...out, [curr]: obj[curr] } : out;
}, {});
}
componentDidMount () {
this.setState(updateSubProperty("textArea",
filterNullProperties(
desc: window.localStorage.getItem('desc'),
pic: window.localStorage.getItem('pic'),
foo: window.localStorage.getItem('foo')
)
));
}
这种方式增加了一些复杂性,但(在我看来)提供了一个真正可读的组件,让我们未来的自己清楚我们正在努力实现的目标。