React In Depth - React 源码解析 2 - setState()
文章目录

Preparation

V16.8.6

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
import React from 'react';
import logo from './logo.svg';
import './App.css';
 
class App extends React.Component{
  constructor(props){
    super(props);
    this.handleClick = this.handleClick.bind(this);
  }
 
 
  handleClick() {
    this.setState({a1})
  }
 
  render = () => (
    <div className="App">
      <header className="App-header">
        <button
          onClick={this.handleClick}
        >
          Learn React
        </button>
      </header>
    </div>
  );
}
 
export default App;

setState

1
2
3
4
5
6
7
8
9
10
11
Component.prototype.setState = function (partialState, callback{
  // 可以看到参数仅仅接收 object/null/function 否则会报错
  (function ({
    if (!(typeof partialState === 'object' || typeof partialState === 'function' || partialState == null)) {
      {
        throw ReactError(Error('setState(...): takes an object of state variables to update or a function which returns an object of state variables.'));
      }
    }
  })();
  this.updater.enqueueSetState(this, partialState, callback, 'setState');
};

然后调用到了 this.updater.enqueueSetState():

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
enqueueSetState: function (inst, payload, callback{
  var fiber = get(inst);
  var currentTime = requestCurrentTime();
  var suspenseConfig = requestCurrentSuspenseConfig();
  var expirationTime = computeExpirationForFiber(currentTime, fiber, suspenseConfig);
  var update = createUpdate(expirationTime, suspenseConfig);
  update.payload = payload;
 
  if (callback !== undefined &amp;&amp; callback !== null) {
    {
      warnOnInvalidCallback$1(callback, 'setState');
    }
    update.callback = callback;
  }
 
  if (revertPassiveEffectsChange) {
    flushPassiveEffects();
  }
 
  enqueueUpdate(fiber, update);
  scheduleWork(fiber, expirationTime);
},

同样的会首先执行 enqueueUpdate 将更新放到队列中, 然后 scheduleWork() 计划更新

enqueueUpdate的作用是将传入的更新任务(包含新state以及到期时间的对象)添加到更新队列。

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
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
export function enqueueUpdate<State>(fiber: Fiber, update: Update<State>{
  // Update queues are created lazily.
  // 使用alternate属性双向连接一个当前fiber和其work-in-progress,当前fiber实例的alternate属性指向其work-in-progress,work-in-progress的alternate属性指向当前稳定fiber。
  const alternate = fiber.alternate;
  let queue1;
  let queue2;
  if (alternate === null) {
    // There's only one fiber.
    queue1 = fiber.updateQueue;
    queue2 = null;
    if (queue1 === null) {
      //如果当前组件没有等待setState的队列则创建一个,
        // 利用fiber当前已经记录并需要整合的state存储到queue1与fiber.updateQueue
      queue1 = fiber.updateQueue = createUpdateQueue(fiber.memoizedState);
    }
  } else {
    // There are two owners.
    // 如果fiber树以及workinprogress树都存在,下面的逻辑则会同步两个树的update队列
    queue1 = fiber.updateQueue;
    queue2 = alternate.updateQueue;
    // 当两个树的队列至少有一个不存在的时候执行队列创建或者复制操作
    if (queue1 === null) {
      if (queue2 === null) {
        // Neither fiber has an update queue. Create new ones.
        //  两个队列都没有则根据各自的memoizedState创建update队列
        queue1 = fiber.updateQueue = createUpdateQueue(fiber.memoizedState);
        queue2 = alternate.updateQueue = createUpdateQueue(
          alternate.memoizedState,
        );
      } else {
        // 如果有一个没有则复制另一个队列给它
        // Only one fiber has an update queue. Clone to create a new one.
        queue1 = fiber.updateQueue = cloneUpdateQueue(queue2);
      }
    } else {
      if (queue2 === null) {
        // 如果有一个没有则复制另一个队列给它
        // Only one fiber has an update queue. Clone to create a new one.
        queue2 = alternate.updateQueue = cloneUpdateQueue(queue1);
      } else {
        // Both owners have an update queue.
      }
    }
  }

  if (queue2 === null || queue1 === queue2) {
    // There's only a single queue.
    // 如果只有一个树,或者两棵树队列是同一个,则将传入的更新对象添加到第一个队列中
    appendUpdateToQueue(queue1, update);
  } else {
    // There are two queues. We need to append the update to both queues,
    // while accounting for the persistent structure of the list — we don't
    // want the same update to be added multiple times.
    //  如果两个队列存在,则将更新任务加入两个队列中,并避免被添加多次
    if (queue1.lastUpdate === null || queue2.lastUpdate === null) {
      // One of the queues is not empty. We must add the update to both queues.
      //  有一个队列不为空,将update添加到两个队列
      appendUpdateToQueue(queue1, update);
      appendUpdateToQueue(queue2, update);
    } else {
      // Both queues are non-empty. The last update is the same in both lists,
      // because of structural sharing. So, only append to one of the lists.
      appendUpdateToQueue(queue1, update);
      // But we still need to update the `lastUpdate` pointer of queue2.
      queue2.lastUpdate = update;
    }
  }
}

scheduleWork

scheduleWork 做了这么几个操作:

  1. 找到 Fiber 的 root
  2. 刷新一遍当前 Fiber 里面的所有 ExpirationTime
  3. 调用 requestWork

(TODO: 添加代码)

requestWork

performWork