Skip to content

Commit

Permalink
Separate priority for updates
Browse files Browse the repository at this point in the history
When resetting the priority in the complete phase, check the priority of
the update queue so that updates aren't dropped.

Updates inside render, child cWRP, etc are no longer dropped.

The next step is sort the queue by priority and only flush updates that
match the current priority level.
  • Loading branch information
acdlite committed Dec 9, 2016
1 parent 7918da5 commit fdfadf1
Show file tree
Hide file tree
Showing 8 changed files with 187 additions and 93 deletions.
5 changes: 0 additions & 5 deletions scripts/fiber/tests-failing.txt
Original file line number Diff line number Diff line change
Expand Up @@ -78,16 +78,11 @@ src/renderers/shared/shared/__tests__/ReactComponentLifeCycle-test.js
* should carry through each of the phases of setup

src/renderers/shared/shared/__tests__/ReactCompositeComponent-test.js
* should warn about `setState` in render
* should warn about `setState` in getChildContext
* should update refs if shouldComponentUpdate gives false

src/renderers/shared/shared/__tests__/ReactCompositeComponentNestedState-test.js
* should provide up to date values for props

src/renderers/shared/shared/__tests__/ReactCompositeComponentState-test.js
* should update state when called from child cWRP

src/renderers/shared/shared/__tests__/ReactEmptyComponent-test.js
* should still throw when rendering to undefined
* throws when rendering null at the top level
Expand Down
2 changes: 2 additions & 0 deletions scripts/fiber/tests-passing-except-dev.txt
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,8 @@ src/renderers/shared/shared/__tests__/ReactComponentLifeCycle-test.js
src/renderers/shared/shared/__tests__/ReactCompositeComponent-test.js
* should warn about `forceUpdate` on unmounted components
* should warn about `setState` on unmounted components
* should warn about `setState` in render
* should warn about `setState` in getChildContext
* should disallow nested render calls
* should warn when mutated props are passed

Expand Down
1 change: 1 addition & 0 deletions scripts/fiber/tests-passing.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1323,6 +1323,7 @@ src/renderers/shared/shared/__tests__/ReactCompositeComponentState-test.js
* should support setting state
* should call componentDidUpdate of children first
* should batch unmounts
* should update state when called from child cWRP

src/renderers/shared/shared/__tests__/ReactEmptyComponent-test.js
* should not produce child DOM nodes for null and false
Expand Down
25 changes: 12 additions & 13 deletions src/renderers/shared/fiber/ReactFiberBeginWork.js
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,8 @@ var ReactFiberClassComponent = require('ReactFiberClassComponent');
module.exports = function<T, P, I, TI, C, CX>(
config : HostConfig<T, P, I, TI, C, CX>,
hostContext : HostContext<C, CX>,
scheduleUpdate : (fiber: Fiber) => void
scheduleUpdateAtPriority : (fiber: Fiber, priorityLevel : PriorityLevel) => void,
getPriorityContext : () => PriorityLevel
) {

const { shouldSetTextContent } = config;
Expand All @@ -84,7 +85,7 @@ module.exports = function<T, P, I, TI, C, CX>(
mountClassInstance,
resumeMountClassInstance,
updateClassInstance,
} = ReactFiberClassComponent(scheduleUpdate);
} = ReactFiberClassComponent(scheduleUpdateAtPriority, getPriorityContext);

function markChildAsProgressed(current, workInProgress, priorityLevel) {
// We now have clones. Let's store them as the currently progressed work.
Expand Down Expand Up @@ -195,20 +196,20 @@ module.exports = function<T, P, I, TI, C, CX>(
return workInProgress.child;
}

function updateClassComponent(current : ?Fiber, workInProgress : Fiber) {
function updateClassComponent(current : ?Fiber, workInProgress : Fiber, priorityLevel : PriorityLevel) {
let shouldUpdate;
if (!current) {
if (!workInProgress.stateNode) {
// In the initial pass we might need to construct the instance.
constructClassInstance(workInProgress);
mountClassInstance(workInProgress);
mountClassInstance(workInProgress, priorityLevel);
shouldUpdate = true;
} else {
// In a resume, we'll already have an instance we can reuse.
shouldUpdate = resumeMountClassInstance(workInProgress);
shouldUpdate = resumeMountClassInstance(workInProgress, priorityLevel);
}
} else {
shouldUpdate = updateClassInstance(current, workInProgress);
shouldUpdate = updateClassInstance(current, workInProgress, priorityLevel);
}

// Schedule side-effects
Expand Down Expand Up @@ -301,7 +302,7 @@ module.exports = function<T, P, I, TI, C, CX>(
}
}

function mountIndeterminateComponent(current, workInProgress) {
function mountIndeterminateComponent(current, workInProgress, priorityLevel) {
if (current) {
throw new Error('An indeterminate component should never have mounted.');
}
Expand All @@ -322,7 +323,7 @@ module.exports = function<T, P, I, TI, C, CX>(
// Proceed under the assumption that this is a class instance
workInProgress.tag = ClassComponent;
adoptClassInstance(workInProgress, value);
mountClassInstance(workInProgress);
mountClassInstance(workInProgress, priorityLevel);
ReactCurrentOwner.current = workInProgress;
value = value.render();
} else {
Expand Down Expand Up @@ -501,11 +502,11 @@ module.exports = function<T, P, I, TI, C, CX>(

switch (workInProgress.tag) {
case IndeterminateComponent:
return mountIndeterminateComponent(current, workInProgress);
return mountIndeterminateComponent(current, workInProgress, priorityLevel);
case FunctionalComponent:
return updateFunctionalComponent(current, workInProgress);
case ClassComponent:
return updateClassComponent(current, workInProgress);
return updateClassComponent(current, workInProgress, priorityLevel);
case HostRoot: {
const root = (workInProgress.stateNode : FiberRoot);
if (root.pendingContext) {
Expand All @@ -518,9 +519,7 @@ module.exports = function<T, P, I, TI, C, CX>(
}

if (updateQueue) {
// The last three arguments are unimportant because there should be
// no update functions in a HostRoot's queue.
beginUpdateQueue(workInProgress, updateQueue, null, null, null);
beginUpdateQueue(workInProgress, updateQueue, null, null, null, priorityLevel);
}

pushHostContainer(workInProgress.stateNode.containerInfo);
Expand Down
80 changes: 49 additions & 31 deletions src/renderers/shared/fiber/ReactFiberClassComponent.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,13 @@
'use strict';

import type { Fiber } from 'ReactFiber';
import type { UpdateQueue } from 'ReactFiberUpdateQueue';
import type { PriorityLevel } from 'ReactPriorityLevel';

var {
getMaskedContext,
} = require('ReactFiberContext');
var {
createUpdateQueue,
ensureUpdateQueue,
addUpdate,
addReplaceUpdate,
addForceUpdate,
Expand All @@ -36,44 +36,41 @@ var invariant = require('invariant');

const isArray = Array.isArray;

module.exports = function(scheduleUpdate : (fiber: Fiber) => void) {

function scheduleUpdateQueue(fiber: Fiber, updateQueue: UpdateQueue) {
fiber.updateQueue = updateQueue;
// Schedule update on the alternate as well, since we don't know which tree
// is current.
if (fiber.alternate) {
fiber.alternate.updateQueue = updateQueue;
}
scheduleUpdate(fiber);
}
module.exports = function(
scheduleUpdateAtPriority : (fiber: Fiber, priorityLevel : PriorityLevel) => void,
getPriorityContext : () => PriorityLevel,
) {

// Class component state updater
const updater = {
isMounted,
enqueueSetState(instance, partialState) {
const fiber = ReactInstanceMap.get(instance);
const queue = fiber.updateQueue || createUpdateQueue();
addUpdate(queue, partialState);
scheduleUpdateQueue(fiber, queue);
const queue = ensureUpdateQueue(fiber);
const priorityLevel = getPriorityContext();
addUpdate(queue, partialState, priorityLevel);
scheduleUpdateAtPriority(fiber, priorityLevel);
},
enqueueReplaceState(instance, state) {
const fiber = ReactInstanceMap.get(instance);
const queue = fiber.updateQueue || createUpdateQueue();
addReplaceUpdate(queue, state);
scheduleUpdateQueue(fiber, queue);
const queue = ensureUpdateQueue(fiber);
const priorityLevel = getPriorityContext();
addReplaceUpdate(queue, state, priorityLevel);
scheduleUpdateAtPriority(fiber, priorityLevel);
},
enqueueForceUpdate(instance) {
const fiber = ReactInstanceMap.get(instance);
const queue = fiber.updateQueue || createUpdateQueue();
addForceUpdate(queue);
scheduleUpdateQueue(fiber, queue);
const queue = ensureUpdateQueue(fiber);
const priorityLevel = getPriorityContext();
addForceUpdate(queue, priorityLevel);
scheduleUpdateAtPriority(fiber, priorityLevel);
},
enqueueCallback(instance, callback) {
const fiber = ReactInstanceMap.get(instance);
const queue = fiber.updateQueue || createUpdateQueue();
addCallback(queue, callback);
scheduleUpdateQueue(fiber, queue);
const queue = ensureUpdateQueue(fiber);
const priorityLevel = getPriorityContext();
addCallback(queue, callback, priorityLevel);
scheduleUpdateAtPriority(fiber, priorityLevel);
},
};

Expand Down Expand Up @@ -219,7 +216,7 @@ module.exports = function(scheduleUpdate : (fiber: Fiber) => void) {
}

// Invokes the mount life-cycles on a previously never rendered instance.
function mountClassInstance(workInProgress : Fiber) : void {
function mountClassInstance(workInProgress : Fiber, priorityLevel : PriorityLevel) : void {
const instance = workInProgress.stateNode;
const state = instance.state || null;

Expand All @@ -238,14 +235,21 @@ module.exports = function(scheduleUpdate : (fiber: Fiber) => void) {
// process them now.
const updateQueue = workInProgress.updateQueue;
if (updateQueue) {
instance.state = beginUpdateQueue(workInProgress, updateQueue, instance, state, props);
instance.state = beginUpdateQueue(
workInProgress,
updateQueue,
instance,
state,
props,
priorityLevel
);
}
}
}

// Called on a preexisting class instance. Returns false if a resumed render
// could be reused.
function resumeMountClassInstance(workInProgress : Fiber) : boolean {
function resumeMountClassInstance(workInProgress : Fiber, priorityLevel : PriorityLevel) : boolean {
let newState = workInProgress.memoizedState;
let newProps = workInProgress.pendingProps;
if (!newProps) {
Expand Down Expand Up @@ -287,13 +291,20 @@ module.exports = function(scheduleUpdate : (fiber: Fiber) => void) {
// during initial mounting.
const newUpdateQueue = workInProgress.updateQueue;
if (newUpdateQueue) {
newInstance.state = beginUpdateQueue(workInProgress, newUpdateQueue, newInstance, newState, newProps);
newInstance.state = beginUpdateQueue(
workInProgress,
newUpdateQueue,
newInstance,
newState,
newProps,
priorityLevel
);
}
return true;
}

// Invokes the update life-cycles and returns false if it shouldn't rerender.
function updateClassInstance(current : Fiber, workInProgress : Fiber) : boolean {
function updateClassInstance(current : Fiber, workInProgress : Fiber, priorityLevel : PriorityLevel) : boolean {
const instance = workInProgress.stateNode;

const oldProps = workInProgress.memoizedProps || current.memoizedProps;
Expand Down Expand Up @@ -325,7 +336,14 @@ module.exports = function(scheduleUpdate : (fiber: Fiber) => void) {
// TODO: Previous state can be null.
let newState;
if (updateQueue) {
newState = beginUpdateQueue(workInProgress, updateQueue, instance, oldState, newProps);
newState = beginUpdateQueue(
workInProgress,
updateQueue,
instance,
oldState,
newProps,
priorityLevel
);
} else {
newState = oldState;
}
Expand Down
14 changes: 9 additions & 5 deletions src/renderers/shared/fiber/ReactFiberReconciler.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ var {
var { createFiberRoot } = require('ReactFiberRoot');
var ReactFiberScheduler = require('ReactFiberScheduler');

var { createUpdateQueue, addCallback } = require('ReactFiberUpdateQueue');
var { ensureUpdateQueue, addCallback } = require('ReactFiberUpdateQueue');

if (__DEV__) {
var ReactFiberInstrumentation = require('ReactFiberInstrumentation');
Expand Down Expand Up @@ -99,6 +99,7 @@ module.exports = function<T, P, I, TI, C, CX>(config : HostConfig<T, P, I, TI, C

var {
scheduleWork,
getPriorityContext,
performWithPriority,
batchedUpdates,
syncUpdates,
Expand All @@ -114,13 +115,15 @@ module.exports = function<T, P, I, TI, C, CX>(config : HostConfig<T, P, I, TI, C
// TODO: Use the updateQueue and scheduleUpdate, instead of pendingProps.
// TODO: This should not override the pendingWorkPriority if there is
// higher priority work in the subtree.

current.pendingProps = element;
if (current.alternate) {
current.alternate.pendingProps = element;
}
if (callback) {
const queue = current.updateQueue || createUpdateQueue();
addCallback(queue, callback);
const queue = ensureUpdateQueue(current);
const priorityLevel = getPriorityContext();
addCallback(queue, callback, priorityLevel);
current.updateQueue = queue;
if (current.alternate) {
current.alternate.updateQueue = queue;
Expand Down Expand Up @@ -154,8 +157,9 @@ module.exports = function<T, P, I, TI, C, CX>(config : HostConfig<T, P, I, TI, C
current.alternate.pendingProps = element;
}
if (callback) {
const queue = current.updateQueue || createUpdateQueue();
addCallback(queue, callback);
const queue = ensureUpdateQueue(current);
const priorityLevel = getPriorityContext();
addCallback(queue, callback, priorityLevel);
current.updateQueue = queue;
if (current.alternate) {
current.alternate.updateQueue = queue;
Expand Down
Loading

0 comments on commit fdfadf1

Please sign in to comment.