React中setState几个现象---先知道再理解

常规情况

在同一个方法中多次 setState 是会被合并的,并且对相同属性的设置只保留最后一次的设置;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
import React from "react";

export class Test extends React.Component {
constructor(props) {
super(props);
this.state = {
count: 0,
};
}
componentWillMount() {
let me = this;
me.setState({
count: me.state.count + 2,
});
me.setState({
count: me.state.count + 1,
});
}
componentDidMount() {
let me = this;
me.setState({
count: me.state.count + 2,
});
me.setState({
count: me.state.count + 1,
});
}

onClick() {
let me = this;
me.setState({
count: me.state.count + 1,
});
me.setState({
count: me.state.count + 1,
});
}

render() {
console.log(this.state.count);
console.log("1111111111111111111111111111111111111111111");
return (
<div>
<h1>{this.state.count}</h1>
<input type="button" value="点击我" onClick={this.onClick.bind(this)} />
<br />
<br />
</div>
);
}
}

上述执行过程如下:

  1. willmount中的setState会合并成一次执行,count只会保留最后一次的设置,前面的放弃,所以willmount之后是1,并不是3;并且在render之前执行,不会引起新的render
  2. render 之后执行 didMount,setState 做同样的处理,这是count2,并且引起新的 render
  3. 点击按钮,setState做同样处理,count3,引起新的render

###定时器中的 setState
定时器中的 setState,每次都会引起新的 render,即使是同一个定时器中的多次 setState

代码更改成如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
componentWillMount() {
let me = this;
setTimeout(() => {
me.setState({
count: me.state.count + 1
});
me.setState({
count: me.state.count + 1
});
}, 0);
}

componentDidMount() {
let me = this;
setTimeout(() => {
me.setState({
count: me.state.count + 1
});
me.setState({
count: me.state.count + 1
});
}, 0);
}

onClickTime() {
let me = this;
setTimeout(() => {
me.setState({
count: me.state.count + 1
});
me.setState({
count: me.state.count + 1
});
}, 0);
}

上述代码,每次setState都会引发新的 render,需要深入了解的可以查查setState的原理,简单理解是定时器中的setState没走react的事物机制,执行时批量更新没被设置true,所以每次都直接 render 了。

原生事件中的 setState

  • 在按钮原生事件中定义的setState,和定时器效果一样,每次setState都会引起新的render
  • react 事件是合并的成一次 render 的。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
componentDidMount() {
this.button.addEventListener('click', this.onClick.bind(this, '原生事件'), false);
}

onClick(info) {
console.log(info);
this.setState({
count: ++count
});
this.setState({
count: ++count
});
}

render() {
console.log(this.state.count);
return <div>
<input type="button" ref={input => this.button = input} onClick={this.onClick.bind(this, 'React事件')} value="生成计时器" />
<div>Count:{this.state.count}</div>
</div>
}

点击按钮,先执行原生事件,再执行 react 事件,但是原生事件会触发两次 render,react 事件触发一次。

总结

上述是我对 setState 的理解,抛砖引玉,希望帮助大家有方向的去了解 react 原理机制。刚开始接触,很多同学想深入了解,但可能不知道从何入手,这也是我遇到过的困扰,所以现在分享出来,希望能帮助大家少走弯路,更快的、更有准针对性的去研究学习 React。


以下为补充内容

原因是什么?

原因是:执行上下文发生了变化。

在事件执行的时候,当前上下文执行的是setTimeout函数,但当执行setTimeout函数的回调时,原来的上下文已经结束了,回调的上下文变成了 window,所以依据的批量更新属性isBatchingUpdates没有被设置成true的过程,因此始终是false,因此setState就同步执行了。

回调不会触发 react 的批量更新机制

其实在回调函数中,setState 是不会触发批量更新机制的,无论是 promise,ajax,setTimeout 回调等等,同时设置多次 setState,每个 setState 都会单独执行并 render,因为上下文发生了变化。

下面是验证 code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
onClickBtn = () => {
// const promise = new Promise((resolve, reject) => {
// this.setState({ count: this.state.count + 1 });
// console.log(this.state.count);
// this.setState({ count: this.state.count + 1 });
// console.log(this.state.count);
// resolve();
// });

// promise.then(() => {
// this.setState({ count: this.state.count + 1 });
// console.log(this.state.count);
// this.setState({ count: this.state.count + 1 });
// console.log(this.state.count);
// });

fetch("/api/getlist")
.then(response => {
return response.json();
})
.then(data => {
console.log(JSON.stringify(data));
this.setState({ count: this.state.count + 1 });
console.log(this.state.count);
this.setState({ count: this.state.count + 1 });
console.log(this.state.count);
});
};

生命周期和事件中多次 setState 的区别

在写 demo 时发现,虽然didMount中的多次setState会被合并,符合正常的规律,但是通过调试发现,在didMountisBatchingUpdates始终是false,而事件调用触发的setStateisBatchingUpdates则是true

文章作者: wenmu
文章链接: http://blog.wangpengpeng.site/2018/02/28/React%E4%B8%ADsetState%E5%87%A0%E4%B8%AA%E7%8E%B0%E8%B1%A1-%E5%85%88%E7%9F%A5%E9%81%93%E5%86%8D%E7%90%86%E8%A7%A3/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 温木的博客
微信打赏