Batch nested updates when in sync mode

Almost working...
This commit is contained in:
Andrew Clark committed 2016-11-02 12:40:21 -07:00
1 parent 78aef38bfa
commit ecf468e556
4 files changed
+46 -23

No files matched your search

+2
View File
@@ -112,6 +112,8 @@ var DOMRenderer = ReactFiberReconciler({
scheduleDeferredCallback: window.requestIdleCallback,
useSyncScheduling: true,
});
var warned = false;
@@ -130,7 +130,7 @@ module.exports = function(scheduleUpdate : (fiber: Fiber, priorityLevel : ?Prior
// process them now.
const updateQueue = workInProgress.updateQueue;
if (updateQueue) {
instance.state = mergeUpdateQueue(updateQueue, state, props);
instance.state = mergeUpdateQueue(updateQueue, instance, state, props);
}
}
}
@@ -174,7 +174,7 @@ module.exports = function(scheduleUpdate : (fiber: Fiber, priorityLevel : ?Prior
// process them now.
const newUpdateQueue = workInProgress.updateQueue;
if (newUpdateQueue) {
newInstance.state = mergeUpdateQueue(newUpdateQueue, newState, newProps);
newInstance.state = mergeUpdateQueue(newUpdateQueue, newInstance, newState, newProps);
}
}
return true;
@@ -211,7 +211,7 @@ module.exports = function(scheduleUpdate : (fiber: Fiber, priorityLevel : ?Prior
// TODO: Previous state can be null.
let newState;
if (updateQueue) {
newState = mergeUpdateQueue(updateQueue, previousState, newProps);
newState = mergeUpdateQueue(updateQueue, instance, previousState, newProps);
} else {
newState = previousState;
}
@@ -71,6 +71,9 @@ module.exports = function<T, P, I, TI, C>(config : HostConfig<T, P, I, TI, C>) {
SynchronousPriority :
LowPriority;
// Whether updates should be batched. Only applies when using sync scheduling.
let shouldBatchUpdates : boolean = false;
// The next work in progress fiber that we're currently working on.
let nextUnitOfWork : ?Fiber = null;
let nextPriorityLevel : PriorityLevel = NoWork;
@@ -490,9 +493,10 @@ module.exports = function<T, P, I, TI, C>(config : HostConfig<T, P, I, TI, C>) {
while (nextUnitOfWork &&
nextPriorityLevel === SynchronousPriority) {
nextUnitOfWork = performUnitOfWork(nextUnitOfWork, false);
// If there's no nextUnitOfWork, we don't need to search for more
// because it shouldn't be possible to schedule sync work without
// immediately performing it
if (!nextUnitOfWork) {
nextUnitOfWork = findNextUnitOfWork();
}
}
if (nextUnitOfWork) {
if (nextPriorityLevel > AnimationPriority) {
@@ -504,7 +508,35 @@ module.exports = function<T, P, I, TI, C>(config : HostConfig<T, P, I, TI, C>) {
}
function performSynchronousWork() {
if (useSyncScheduling) {
// Start batching updates
shouldBatchUpdates = true;
}
performAndHandleErrors(performSynchronousWorkUnsafe);
shouldBatchUpdates = false;
}
function scheduleSynchronousWork(root : FiberRoot) {
root.current.pendingWorkPriority = SynchronousPriority;
if (root.isScheduled) {
// If we're already scheduled, we can bail out.
return;
}
root.isScheduled = true;
if (lastScheduledRoot) {
// Schedule ourselves to the end.
lastScheduledRoot.nextScheduledRoot = root;
lastScheduledRoot = root;
} else {
// We're the only work scheduled.
nextScheduledRoot = root;
lastScheduledRoot = root;
if (!shouldBatchUpdates) {
performSynchronousWork();
}
}
}
function performAndHandleErrors<A>(fn: (a: A) => void, a: A) {
@@ -560,10 +592,9 @@ module.exports = function<T, P, I, TI, C>(config : HostConfig<T, P, I, TI, C>) {
// We will process an update caused by each error boundary synchronously.
affectedBoundaries.forEach(boundary => {
// FIXME: We only specify LowPriority here so that setState() calls from the error
// boundaries are respected. Instead we should set default priority level or something
// like this. Reconsider this piece when synchronous scheduling is in place.
const priority = LowPriority;
const priority = priorityContext !== null ?
priorityContext :
defaultPriorityContext;
const root = scheduleErrorBoundaryWork(boundary, priority);
// This should use findNextUnitOfWork() when synchronous scheduling is implemented.
let fiber = cloneFiber(root.current, priority);
@@ -604,8 +635,7 @@ module.exports = function<T, P, I, TI, C>(config : HostConfig<T, P, I, TI, C>) {
}
if (priorityLevel === SynchronousPriority) {
root.current.pendingWorkPriority = SynchronousPriority;
performSynchronousWork();
scheduleSynchronousWork(root);
}
if (priorityLevel === NoWork) {
@@ -626,15 +656,6 @@ module.exports = function<T, P, I, TI, C>(config : HostConfig<T, P, I, TI, C>) {
defaultPriorityContext;
}
// Don't bother bubbling the priority to the root if it is synchronous. Just
// perform it now.
if (priorityLevel === SynchronousPriority) {
fiber.pendingWorkPriority = SynchronousPriority;
nextUnitOfWork = fiber;
performSynchronousWork();
return;
}
while (true) {
if (fiber.pendingWorkPriority === NoWork ||
fiber.pendingWorkPriority >= priorityLevel) {
@@ -73,14 +73,14 @@ exports.callCallbacks = function(queue : UpdateQueue, context : any) {
}
};
exports.mergeUpdateQueue = function(queue : UpdateQueue, prevState : any, props : any) : any {
exports.mergeUpdateQueue = function(queue : UpdateQueue, instance : any, prevState : any, props : any) : any {
let node : ?UpdateQueueNode = queue;
let state = queue.isReplace ? null : Object.assign({}, prevState);
while (node) {
let partialState;
if (typeof node.partialState === 'function') {
const updateFn = node.partialState;
partialState = updateFn(state, props);
partialState = updateFn.call(instance, state, props);
} else {
partialState = node.partialState;
}