目录

  1. 回顾 setState API
  2. setState 的异步
  3. 难道同步就不能累积更新、批量处理了吗?

    前言

    setState 这个 API 对于每一个 React 开发者来说既陌生有熟悉。熟悉是当你入门 React 的时候,接触的最早的 API 里一定有 setState(数据驱动视图)。陌生是当你数据出现问题,可能最后发现问题出在 setState。setState 真的是一个让 React 开发者们又熟悉有陌生的 API 。

我们在 React 使用 setState 时,知道它并不总是异步更新,也可能是同步更新,大家也知道 setState 使用异步是为累积更新、批量处理、减少调用次数来提升性能,但是大家有没有想过 setState 的异步更新真的单单是为了性能吗?
image.png

1. 回顾 setState API

首先我们简单回顾一下 setState API 。

  1. setState(updater, [callback])

updater
  1. (state, props) => stateChange

updater 函数中接收的 state 和 props 都保证为最新。updater 的返回值会与 state 进行浅合并。

callback

setState() 的第二个参数为可选的回调函数,它将在 setState 完成合并并重新渲染组件后执行。通常,我们建议使用 componentDidUpdate() 来代替此方式。

2. setState 的异步

setState 将对组件 state 的更改排入队列,并通知 React 需要使用更新后的 state 重新渲染组件及其子组件。为了更好的性能,setState 并不是立即进行更新,而是使用批量推迟更新。

看几个异步的例子,这些都是经常出现在面试中的代码例子(源码测试

  1. class Test extends Component {
  2. state = {
  3. count: 0
  4. };
  5. componentDidMount() {
  6. this.setState({
  7. count: 1
  8. }, () => {
  9. console.log(this.state.count); //1
  10. }
  11. );
  12. console.log(this.state.count); // 0
  13. }
  14. render() {}
  15. }
  1. class Test extends Component {
  2. state = {
  3. count: 0
  4. };
  5. componentDidMount() {
  6. this.setState(
  7. {
  8. count: this.state.count + 1
  9. },
  10. () => {
  11. console.log(this.state.count); // 1
  12. }
  13. );
  14. this.setState(
  15. {
  16. count: this.state.count + 1 // 这里的count还是0,并不会拿到更新后的count
  17. },
  18. () => {
  19. console.log(this.state.count); // 1
  20. }
  21. );
  22. }
  23. render() {}
  24. }
  1. class Test extends React.Component {
  2. state = {
  3. count: 0
  4. };
  5. componentDidMount() {
  6. this.setState(
  7. (preState) => {
  8. console.log(preState.count); // 0
  9. return {
  10. count: preState.count + 1
  11. };
  12. },
  13. () => {
  14. console.log(this.state.count); // 2
  15. }
  16. );
  17. this.setState(
  18. (preState) => {
  19. console.log(preState.count); // 1
  20. return {
  21. count: preState.count + 1
  22. };
  23. },
  24. () => {
  25. console.log(this.state.count); // 2
  26. }
  27. );
  28. }
  29. render() {
  30. return <div>1</div>;
  31. }
  32. }

当调用setState函数时,就会把当前的操作放入到队列中,React 根据内容合并state数据,完成之后在逐一执行回调,根据结果去更新需求DOM,触发渲染。这里采用的是异步的方法,根据说法是异步是为了累积更新,批量处理,减少渲染次数,提升性能。

但是这里其实大家有没有一个疑问,异步可以**累积更新、批量处理**,难道同步就不能累积更新、批量处理了吗?难道同步就不能减少渲染次数,提升性了吗?
**
image.png

3. 难道同步就不能累积更新、批量处理了吗

这个问题其实不止我有疑问,很早之前有大佬就已经在Issues上有提出这个问题:
image.png
当然在这个 Issues 下面 gaearon 大佬回复了这个问题:
image.png
image.png
image.png
回答的比较长,大体总结了两个方面:

1. 保持一致性

如果改为同步更新的方法,虽然 setState 是同步的,但是 props 不是。

2. 为后续的架构升级启用并发更新

为了完成一部的渲染,React 会在触发 setState 的时候更新数据来源分配不同的优先级。这些数据的来源有:事件回调句柄、动画效果等,在根据优先级并发处理,提升渲染性能。

总结

setState 异步的一个重要的动机——避免频繁的 re-render,提升性能**但是这并不是全部更深层次的目的是为了保持一致性为以后需的架构升级启动并发更新**。所以React setState 异步真的不单单只是为了性能吗,还是有其他原因。

参考