setState 是如何给 state 赋值的
- 通过
Object.assign()
import React from 'react';
class Home extends React.Component {
constructor(props) {
super(props);
this.state = {
name: 'BNTang',
age: 18
}
let oldObj = {name: 'BNTang', age: 18};
let newObj = {age: 666};
let obj = Object.assign({}, oldObj, newObj);
console.log(obj);
}
render() {
return (
<div>
<p>{this.state.name}</p>
<p>{this.state.age}</p>
<button onClick={() => {
this.btnClick()
}}>按钮
</button>
</div>
)
}
btnClick() {
this.setState({
age: 666
});
}
}
class App extends React.Component {
render() {
return (
<div>
<Home/>
</div>
)
}
}
export default App;
state 合并现象
- 因为
setState
会收集一段时间内所有的修改操作,然后在统一的执行,再更新界面 - 所以就出现了 state 的合并现象
首先来看一个案例,然后引出这个 state 的合并场景先如下:
import React from 'react';
class Home extends React.Component {
constructor(props) {
super(props);
this.state = {
age: 0
}
}
render() {
return (
<div>
<p>{this.state.age}</p>
<button onClick={() => {
this.btnClick()
}}>按钮
</button>
</div>
)
}
btnClick() {
this.setState({
age: this.state.age + 1
});
this.setState({
age: this.state.age + 1
});
this.setState({
age: this.state.age + 1
});
console.log(this.state.age);
}
}
class App extends React.Component {
render() {
return (
<div>
<Home/>
</div>
)
}
}
export default App;
然后查看结果发现居然是 1:
为什么最终的一个值是1, 不是 3 呢是吧,我明明是进行增加了 3 次加 1 的操作,因为 setState 默认是一个异步的方法, 默认会收集一段时间内所有的更新, 然后再统一更新, 所以就导致了最终的一个值是 1, 不是 3,博主可以大致的提供一下它底层的实现代码这样可以更加的让你对 setState 有更深层次的理解,如下:
let oldObj = {age: 0};
let stateList = [
// 演变过程1
// {age: oldObj.age + 1},
// {age: oldObj.age + 1},
// {age: oldObj.age + 1},
// 演变过程2
// {age: 0 + 1},
// {age: 0 + 1},
// {age: 0 + 1},
// 演变过程3
{age: 1},
{age: 1},
{age: 1}
];
stateList.forEach((newObj) => {
// 演变过程1
// Object.assign({}, {age: 0}, {age: 1}); // {age: 1}
// 演变过程2
// Object.assign({}, {age: 1}, {age: 1}); // {age: 1}
// 演变过程3
// Object.assign({}, {age: 1}, {age: 1}); // {age: 1}
oldObj = Object.assign({}, oldObj, newObj);
});
console.log(oldObj);
解决 state 合并现象
第一种方案就是前面所说的通过 setState 方法的第二个参数, 通过回调函数拿到更新之后的值,然后在根据该值在进行加一操作如下:
import React from 'react';
class Home extends React.Component {
constructor(props) {
super(props);
this.state = {
age: 0
}
}
render() {
return (
<div>
<p>{this.state.age}</p>
<button onClick={() => {
this.btnClick()
}}>按钮
</button>
</div>
)
}
btnClick() {
this.setState({
age: this.state.age + 1
}, () => {
this.setState({
age: this.state.age + 1
}, () => {
this.setState({
age: this.state.age + 1
});
});
});
console.log(this.state.age);
}
}
class App extends React.Component {
render() {
return (
<div>
<Home/>
</div>
)
}
}
export default App;
但是上面的代码存在弊端,层级结构比较深,以后维护比较不友好,所以说 React 也考虑到了这一点,所以这里就引出了第二种解决方案,通过 setState 的第一个参数来进行解决,第一个参数除了可以传递一个对象,其实还可以传递一个回调函数,回调函数有两个默认的参数第一个就是上一次更新的最新的值,然后我们可以在该回调函数中就可以直接拿到最新的值,就不会出现合并的现象了。
import React from 'react';
class Home extends React.Component {
constructor(props) {
super(props);
this.state = {
age: 0
}
}
render() {
return (
<div>
<p>{this.state.age}</p>
<button onClick={() => {
this.btnClick()
}}>按钮
</button>
</div>
)
}
btnClick() {
this.setState((preState, props) => {
return {age: preState.age + 1};
});
this.setState((preState, props) => {
return {age: preState.age + 1};
});
this.setState((preState, props) => {
return {age: preState.age + 1};
});
console.log(this.state.age);
}
}
class App extends React.Component {
render() {
return (
<div>
<Home/>
</div>
)
}
}
export default App;
那么为什么这样就可以解决从 0 变为 3 呢,这里也提供出它底层的实现,和演变过程,可以更深层次的加深理解:
let oldObj = {age: 0};
let stateList = [
(preState) => {
return {age: preState.age + 1}
},
(preState) => {
return {age: preState.age + 1}
},
(preState) => {
return {age: preState.age + 1}
},
];
stateList.forEach((fn) => {
// 演变过程1
// {age: 1}
// 演变过程2
// {agg: 2}
// 演变过程3
// {agg: 3}
let newObj = fn(oldObj);
// 演变过程1
// {age: 0} {age: 1} / {age: 1}
// 演变过程2
// {age: 1} {age: 2} / {age: 2}
// 演变过程3
// {age: 2} {age: 3} / {age: 3}
oldObj = Object.assign({}, oldObj, newObj);
});
console.log(oldObj);