Break out some Class Component logic into separate module
Refactors the class logic a bit. I moved scheduleUpdate out into the scheduler since that's where the scheduling normally happens. I also moved it so that we can rely on hoisting to resolve the cycle statically. I moved the updater to a new class component file. I suspect we will need a bit of space in here since the class initialization code is quite complex. The class component dependency is currently fixed in BeginWork so we can't move complete or commit phase stuff to it. If we need to, we have to initialize it in the scheduler and pass it to the other phases.
Sebastian Markbage committed
Oct 11, 2016 at 10:43 UTC
bc5dfd53584fecb00d64f21d9fa1977c8cfa45c7
4 files changed
+128
-97
src/renderers/shared/fiber/ReactFiberBeginWork.js
+7
-83
@@ -14,11 +14,8 @@
14
15
import type { ReactCoroutine } from 'ReactCoroutine';
16
import type { Fiber } from 'ReactFiber';
17
-import type { FiberRoot } from 'ReactFiberRoot';
17
import type { HostConfig } from 'ReactFiberReconciler';
19
-import type { Scheduler } from 'ReactFiberScheduler';
18
import type { PriorityLevel } from 'ReactPriorityLevel';
21
-import type { UpdateQueue } from 'ReactFiberUpdateQueue';
19
20
var {
21
mountChildFibersInPlace,
@@ -26,7 +23,6 @@ var {
23
reconcileChildFibersInPlace,
24
cloneChildFibers,
25
} = require('ReactChildFiber');
29
-var { LowPriority } = require('ReactPriorityLevel');
26
var ReactTypeOfWork = require('ReactTypeOfWork');
27
var {
28
IndeterminateComponent,
@@ -45,17 +41,18 @@ var {
41
OffscreenPriority,
42
} = require('ReactPriorityLevel');
43
var {
48
- createUpdateQueue,
49
- addToQueue,
50
- addCallbackToQueue,
44
mergeUpdateQueue,
45
} = require('ReactFiberUpdateQueue');
46
var {
47
Placement,
48
} = require('ReactTypeOfSideEffect');
56
-var ReactInstanceMap = require('ReactInstanceMap');
49
+var ReactFiberClassComponent = require('ReactFiberClassComponent');
50
58
-module.exports = function<T, P, I, TI, C>(config : HostConfig<T, P, I, TI, C>, getScheduler : () => Scheduler) {
51
+module.exports = function<T, P, I, TI, C>(config : HostConfig<T, P, I, TI, C>, scheduleUpdate : (fiber: Fiber, priorityLevel : PriorityLevel) => void) {
52
+
53
+ const {
54
+ mount,
55
+ } = ReactFiberClassComponent(scheduleUpdate);
56
57
function markChildAsProgressed(current, workInProgress, priorityLevel) {
58
// We now have clones. Let's store them as the currently progressed work.
@@ -158,72 +155,6 @@ module.exports = function<T, P, I, TI, C>(config : HostConfig<T, P, I, TI, C>, g
155
return workInProgress.child;
156
}
157
161
- function scheduleUpdate(fiber: Fiber, updateQueue: UpdateQueue, priorityLevel : PriorityLevel): void {
162
- const { scheduleDeferredWork } = getScheduler();
163
- fiber.updateQueue = updateQueue;
164
- // Schedule update on the alternate as well, since we don't know which tree
165
- // is current.
166
- if (fiber.alternate) {
167
- fiber.alternate.updateQueue = updateQueue;
168
- }
169
- while (true) {
170
- if (fiber.pendingWorkPriority === NoWork ||
171
- fiber.pendingWorkPriority >= priorityLevel) {
172
- fiber.pendingWorkPriority = priorityLevel;
173
- }
174
- if (fiber.alternate) {
175
- if (fiber.alternate.pendingWorkPriority === NoWork ||
176
- fiber.alternate.pendingWorkPriority >= priorityLevel) {
177
- fiber.alternate.pendingWorkPriority = priorityLevel;
178
- }
179
- }
180
- // Duck type root
181
- if (fiber.stateNode && fiber.stateNode.containerInfo) {
182
- const root : FiberRoot = (fiber.stateNode : any);
183
- scheduleDeferredWork(root, priorityLevel);
184
- return;
185
- }
186
- if (!fiber.return) {
187
- throw new Error('No root!');
188
- }
189
- fiber = fiber.return;
190
- }
191
- }
192
-
193
- // Class component state updater
194
- const updater = {
195
- enqueueSetState(instance, partialState) {
196
- const fiber = ReactInstanceMap.get(instance);
197
- const updateQueue = fiber.updateQueue ?
198
- addToQueue(fiber.updateQueue, partialState) :
199
- createUpdateQueue(partialState);
200
- scheduleUpdate(fiber, updateQueue, LowPriority);
201
- },
202
- enqueueReplaceState(instance, state) {
203
- const fiber = ReactInstanceMap.get(instance);
204
- const updateQueue = createUpdateQueue(state);
205
- updateQueue.isReplace = true;
206
- scheduleUpdate(fiber, updateQueue, LowPriority);
207
- },
208
- enqueueForceUpdate(instance) {
209
- const fiber = ReactInstanceMap.get(instance);
210
- const updateQueue = fiber.updateQueue || createUpdateQueue(null);
211
- updateQueue.isForced = true;
212
- scheduleUpdate(fiber, updateQueue, LowPriority);
213
- },
214
- enqueueCallback(instance, callback) {
215
- const fiber = ReactInstanceMap.get(instance);
216
- let updateQueue = fiber.updateQueue ?
217
- fiber.updateQueue :
218
- createUpdateQueue(null);
219
- addCallbackToQueue(updateQueue, callback);
220
- fiber.updateQueue = updateQueue;
221
- if (fiber.alternate) {
222
- fiber.alternate.updateQueue = updateQueue;
223
- }
224
- },
225
- };
226
-
158
function updateClassComponent(current : ?Fiber, workInProgress : Fiber) {
159
// A class component update is the result of either new props or new state.
160
// Account for the possibly of missing pending props by falling back to the
@@ -243,15 +174,8 @@ module.exports = function<T, P, I, TI, C>(config : HostConfig<T, P, I, TI, C>, g
174
if (!instance) {
175
var ctor = workInProgress.type;
176
workInProgress.stateNode = instance = new ctor(props);
177
+ mount(workInProgress, instance);
178
state = instance.state || null;
247
- // The initial state must be added to the update queue in case
248
- // setState is called before the initial render.
249
- if (state !== null) {
250
- workInProgress.updateQueue = createUpdateQueue(state);
251
- }
252
- // The instance needs access to the fiber so that it can schedule updates
253
- ReactInstanceMap.set(instance, workInProgress);
254
- instance.updater = updater;
179
} else if (typeof instance.shouldComponentUpdate === 'function' &&
180
!(updateQueue && updateQueue.isForced)) {
181
if (workInProgress.memoizedProps !== null) {
src/renderers/shared/fiber/ReactFiberClassComponent.js
new
+89
@@ -0,0 +1,89 @@
1
+/**
2
+ * Copyright 2013-present, Facebook, Inc.
3
+ * All rights reserved.
4
+ *
5
+ * This source code is licensed under the BSD-style license found in the
6
+ * LICENSE file in the root directory of this source tree. An additional grant
7
+ * of patent rights can be found in the PATENTS file in the same directory.
8
+ *
9
+ * @providesModule ReactFiberClassComponent
10
+ * @flow
11
+ */
12
+
13
+'use strict';
14
+
15
+import type { Fiber } from 'ReactFiber';
16
+import type { PriorityLevel } from 'ReactPriorityLevel';
17
+import type { UpdateQueue } from 'ReactFiberUpdateQueue';
18
+
19
+var { LowPriority } = require('ReactPriorityLevel');
20
+var {
21
+ createUpdateQueue,
22
+ addToQueue,
23
+ addCallbackToQueue,
24
+} = require('ReactFiberUpdateQueue');
25
+var ReactInstanceMap = require('ReactInstanceMap');
26
+
27
+module.exports = function(scheduleUpdate : (fiber: Fiber, priorityLevel : PriorityLevel) => void) {
28
+
29
+ function scheduleUpdateQueue(fiber: Fiber, updateQueue: UpdateQueue, priorityLevel : PriorityLevel) {
30
+ fiber.updateQueue = updateQueue;
31
+ // Schedule update on the alternate as well, since we don't know which tree
32
+ // is current.
33
+ if (fiber.alternate) {
34
+ fiber.alternate.updateQueue = updateQueue;
35
+ }
36
+ scheduleUpdate(fiber, priorityLevel);
37
+ }
38
+
39
+ // Class component state updater
40
+ const updater = {
41
+ enqueueSetState(instance, partialState) {
42
+ const fiber = ReactInstanceMap.get(instance);
43
+ const updateQueue = fiber.updateQueue ?
44
+ addToQueue(fiber.updateQueue, partialState) :
45
+ createUpdateQueue(partialState);
46
+ scheduleUpdateQueue(fiber, updateQueue, LowPriority);
47
+ },
48
+ enqueueReplaceState(instance, state) {
49
+ const fiber = ReactInstanceMap.get(instance);
50
+ const updateQueue = createUpdateQueue(state);
51
+ updateQueue.isReplace = true;
52
+ scheduleUpdateQueue(fiber, updateQueue, LowPriority);
53
+ },
54
+ enqueueForceUpdate(instance) {
55
+ const fiber = ReactInstanceMap.get(instance);
56
+ const updateQueue = fiber.updateQueue || createUpdateQueue(null);
57
+ updateQueue.isForced = true;
58
+ scheduleUpdateQueue(fiber, updateQueue, LowPriority);
59
+ },
60
+ enqueueCallback(instance, callback) {
61
+ const fiber = ReactInstanceMap.get(instance);
62
+ let updateQueue = fiber.updateQueue ?
63
+ fiber.updateQueue :
64
+ createUpdateQueue(null);
65
+ addCallbackToQueue(updateQueue, callback);
66
+ fiber.updateQueue = updateQueue;
67
+ if (fiber.alternate) {
68
+ fiber.alternate.updateQueue = updateQueue;
69
+ }
70
+ },
71
+ };
72
+
73
+ function mount(workInProgress : Fiber, instance : any) {
74
+ const state = instance.state || null;
75
+ // The initial state must be added to the update queue in case
76
+ // setState is called before the initial render.
77
+ if (state !== null) {
78
+ workInProgress.updateQueue = createUpdateQueue(state);
79
+ }
80
+ // The instance needs access to the fiber so that it can schedule updates
81
+ ReactInstanceMap.set(instance, workInProgress);
82
+ instance.updater = updater;
83
+ }
84
+
85
+ return {
86
+ mount,
87
+ };
88
+
89
+};
src/renderers/shared/fiber/ReactFiberScheduler.js
+27
-11
@@ -40,19 +40,11 @@ var {
40
41
var timeHeuristicForUnitOfWork = 1;
42
43
-export type Scheduler = {
44
- scheduleDeferredWork: (root : FiberRoot, priority : PriorityLevel) => void
45
-};
46
-
43
module.exports = function<T, P, I, TI, C>(config : HostConfig<T, P, I, TI, C>) {
44
// Use a closure to circumvent the circular dependency between the scheduler
45
// and ReactFiberBeginWork. Don't know if there's a better way to do this.
50
- let scheduler;
51
- function getScheduler(): Scheduler {
52
- return scheduler;
53
- }
46
55
- const { beginWork } = ReactFiberBeginWork(config, getScheduler);
47
+ const { beginWork } = ReactFiberBeginWork(config, scheduleUpdate);
48
const { completeWork } = ReactFiberCompleteWork(config);
49
const { commitInsertion, commitDeletion, commitWork, commitLifeCycles } =
50
ReactFiberCommitWork(config);
@@ -395,6 +387,31 @@ module.exports = function<T, P, I, TI, C>(config : HostConfig<T, P, I, TI, C>) {
387
scheduleAnimationWork(root, defaultPriority);
388
}
389
390
+ function scheduleUpdate(fiber: Fiber, priorityLevel : PriorityLevel): void {
391
+ while (true) {
392
+ if (fiber.pendingWorkPriority === NoWork ||
393
+ fiber.pendingWorkPriority >= priorityLevel) {
394
+ fiber.pendingWorkPriority = priorityLevel;
395
+ }
396
+ if (fiber.alternate) {
397
+ if (fiber.alternate.pendingWorkPriority === NoWork ||
398
+ fiber.alternate.pendingWorkPriority >= priorityLevel) {
399
+ fiber.alternate.pendingWorkPriority = priorityLevel;
400
+ }
401
+ }
402
+ // Duck type root
403
+ if (fiber.stateNode && fiber.stateNode.containerInfo) {
404
+ const root : FiberRoot = (fiber.stateNode : any);
405
+ scheduleDeferredWork(root, priorityLevel);
406
+ return;
407
+ }
408
+ if (!fiber.return) {
409
+ throw new Error('No root!');
410
+ }
411
+ fiber = fiber.return;
412
+ }
413
+ }
414
+
415
function performWithPriority(priorityLevel : PriorityLevel, fn : Function) {
416
const previousDefaultPriority = defaultPriority;
417
defaultPriority = priorityLevel;
@@ -405,10 +422,9 @@ module.exports = function<T, P, I, TI, C>(config : HostConfig<T, P, I, TI, C>) {
422
}
423
}
424
408
- scheduler = {
425
+ return {
426
scheduleWork: scheduleWork,
427
scheduleDeferredWork: scheduleDeferredWork,
428
performWithPriority: performWithPriority,
429
};
413
- return scheduler;
430
};
src/renderers/shared/fiber/ReactFiberUpdateQueue.js
+5
-3
@@ -39,7 +39,7 @@ exports.createUpdateQueue = function(partialState : mixed) : UpdateQueue {
39
return queue;
40
};
41
42
-exports.addToQueue = function(queue : UpdateQueue, partialState : mixed) : UpdateQueue {
42
+function addToQueue(queue : UpdateQueue, partialState : mixed) : UpdateQueue {
43
const node = {
44
partialState,
45
callback: null,
@@ -49,12 +49,14 @@ exports.addToQueue = function(queue : UpdateQueue, partialState : mixed) : Updat
49
queue.tail.next = node;
50
queue.tail = node;
51
return queue;
52
-};
52
+}
53
+
54
+exports.addToQueue = addToQueue;
55
56
exports.addCallbackToQueue = function(queue : UpdateQueue, callback: Function) : UpdateQueue {
57
if (queue.tail.callback) {
58
// If the tail already as a callback, add an empty node to queue
57
- exports.addToQueue(queue, null);
59
+ addToQueue(queue, null);
60
}
61
queue.tail.callback = callback;
62
return queue;