# setState
setState是同步的还是异步的?在什么场景下是同步?什么场景下是异步?
# 特点
浅合并
批量更新策略
# 浅合并
this.setState({ comments }); // ===> this.setState({ ...this.state, comments })
1
this.setState({ comments })
完整保留了 this.state.posts
,但完全替换换了 this.state.comments
。
# 批量更新策略
为了避免不必要的重新渲染,React 对于 setState
采用了 批量更新策略。
例子:
class App extends Component {
state = {
val: 0
}
componentDidMount () {
this.setState({ val: this.state.val + 1 });
console.log('第 1 次 val: ', this.state.val);
this.setState({ val: this.state.val + 1 });
console.log('第 2 次 val: ', this.state.val);
setTimeout(() => {
this.setState({ val: this.state.val + 1 });
console.log('第 3 次 val: ', this.state.val);
this.setState({ val: this.state.val + 1 });
console.log('第 4 次 val: ', this.state.val);
}, 0)
}
render() {
return null;
}
}
// 0 0 2 3
// 解释:
// 前两次 isBatchingUpdates === true,没有更新 state,输出 0 0
// 后两次 同步更新,输出 2 3
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
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
# 原理
- 当触发 React合成事件 时,会触发一个 事务(
batchedUpdates
),执行isBatchingUpdates = true
;
所以可理解为,“整个合成事件的执行过程” 都处在一个大的事务
在这期间,如果触发了
setState
,都会将 “要更新的Component” 存到dirtyComponents
当 合成事件 的事务结束(
close
) 时,会遍历dirtyComponents
;进而调用updateComponent
更新组件,并执行setState
传入的 callback
不仅是 React合成事件,对于 生命周期 也是同理的。
# 总结
类型 | 方式 | 说明 |
---|---|---|
合成事件、 生命周期函数 (除了 componentDidUpdate ) | 会触发一个大事务 | 异步 |
原生事件、 setTimeout | 执行时 不存在大事务,会立即发起新的批量更新 | 同步 |
setState是异步还是同步的?
- 对于 合成事件、生命周期,是异步;
- 对于
setTimeout
、addEventListener
是 同步 的。
为什么在setTimeout方法中调用setState表现出来是同步?
- 因为setTimeout已经完成了原组件的更新流程,不会放入
dirtyComponents
- 因为setTimeout已经完成了原组件的更新流程,不会放入
setState中传入一个 Function,为何里面的state值是最新的?
- 函数接收的 state 是上轮更新过的state。
// this.setState(prevState => ({ count: prevState.count + 1 })) enqueueSetState(publicInstance, partialState, callback, callerName) { // ... const currentState = this._renderer._newState || publicInstance.state; // 若 setState 传入的是 Function,那 state 则是 currentState (即上一轮更新过的 state) if (typeof partialState === 'function') { partialState = partialState.call( publicInstance, currentState, publicInstance.props, ); } this._renderer._newState = { ...currentState, ...partialState, }; this._renderer.render(this._renderer._element, this._renderer._context); }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 事务(Transaction)
TIP
事务(Transaction) 用 Wrapper 封装要执行的方法,暴露一个perform
方法来调用。
在Wrapper里定义
initialize
和close
方法:它们 分别 会在指定方法
执行前、执行后执行。
*
* wrappers (injected at creation time)
* + +
* | |
* +-----------------|--------|--------------+
* | v | |
* | +---------------+ | |
* | +--| wrapper1 |---|----+ |
* | | +---------------+ v | |
* | | +-------------+ | |
* | | +----| wrapper2 |--------+ |
* | | | +-------------+ | | |
* | | | | | |
* | v v v v | wrapper
* | +---+ +---+ +---------+ +---+ +---+ | invariants
* perform(anyMethod) | | | | | | | | | | | | maintained
* +----------------->|-|---|-|---|-->|anyMethod|---|---|-|---|-|-------->
* | | | | | | | | | | | |
* | | | | | | | | | | | |
* | | | | | | | | | | | |
* | +---+ +---+ +---------+ +---+ +---+ |
* | initialize close |
* +-----------------------------------------+
*
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24