Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Deterministic updates #10715

Merged
merged 10 commits into from
Oct 14, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@
"glob-stream": "^6.1.0",
"gzip-js": "~0.3.2",
"gzip-size": "^3.0.0",
"jasmine-check": "^1.0.0-rc.0",
"jest": "20.1.0-delta.1",
"jest-config": "20.1.0-delta.1",
"jest-jasmine2": "20.1.0-delta.1",
Expand Down
2 changes: 2 additions & 0 deletions scripts/jest/test-framework-setup.js
Original file line number Diff line number Diff line change
Expand Up @@ -68,4 +68,6 @@ if (process.env.REACT_CLASS_EQUIVALENCE_TEST) {
return expectation;
};
global.expectDev = expectDev;

require('jasmine-check').install();
}
4 changes: 2 additions & 2 deletions src/renderers/dom/fiber/__tests__/ReactDOMFiberAsync-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -284,8 +284,8 @@ describe('ReactDOMFiberAsync', () => {

// Flush the async updates
jest.runAllTimers();
expect(container.textContent).toEqual('BCAD');
expect(ops).toEqual(['BC', 'BCAD']);
expect(container.textContent).toEqual('ABCD');
expect(ops).toEqual(['BC', 'ABCD']);
});
});
});
2 changes: 1 addition & 1 deletion src/renderers/noop/ReactNoopEntry.js
Original file line number Diff line number Diff line change
Expand Up @@ -405,7 +405,7 @@ var ReactNoop = {
logHostInstances(container.children, depth + 1);
}

function logUpdateQueue(updateQueue: UpdateQueue, depth) {
function logUpdateQueue(updateQueue: UpdateQueue<mixed>, depth) {
log(' '.repeat(depth + 1) + 'QUEUED UPDATES');
const firstUpdate = updateQueue.first;
if (!firstUpdate) {
Expand Down
2 changes: 1 addition & 1 deletion src/renderers/shared/fiber/ReactFiber.js
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ export type Fiber = {|
memoizedProps: any, // The props used to create the output.

// A queue of state updates and callbacks.
updateQueue: UpdateQueue | null,
updateQueue: UpdateQueue<any> | null,

// The state used to create the output
memoizedState: any,
Expand Down
15 changes: 7 additions & 8 deletions src/renderers/shared/fiber/ReactFiberBeginWork.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ var {
reconcileChildFibersInPlace,
cloneChildFibers,
} = require('ReactChildFiber');
var {beginUpdateQueue} = require('ReactFiberUpdateQueue');
var {processUpdateQueue} = require('ReactFiberUpdateQueue');
var ReactTypeOfWork = require('ReactTypeOfWork');
var {
getMaskedContext,
Expand Down Expand Up @@ -71,8 +71,8 @@ module.exports = function<T, P, I, TI, PI, C, CX, PL>(
config: HostConfig<T, P, I, TI, PI, C, CX, PL>,
hostContext: HostContext<C, CX>,
hydrationContext: HydrationContext<C, CX>,
scheduleUpdate: (fiber: Fiber, expirationTime: ExpirationTime) => void,
getExpirationTime: (fiber: Fiber, forceAsync: boolean) => ExpirationTime,
scheduleWork: (fiber: Fiber, expirationTime: ExpirationTime) => void,
computeExpirationForFiber: (fiber: Fiber) => ExpirationTime,
) {
const {
shouldSetTextContent,
Expand All @@ -95,8 +95,8 @@ module.exports = function<T, P, I, TI, PI, C, CX, PL>(
// resumeMountClassInstance,
updateClassInstance,
} = ReactFiberClassComponent(
scheduleUpdate,
getExpirationTime,
scheduleWork,
computeExpirationForFiber,
memoizeProps,
memoizeState,
);
Expand Down Expand Up @@ -323,12 +323,11 @@ module.exports = function<T, P, I, TI, PI, C, CX, PL>(
const updateQueue = workInProgress.updateQueue;
if (updateQueue !== null) {
const prevState = workInProgress.memoizedState;
const state = beginUpdateQueue(
const state = processUpdateQueue(
current,
workInProgress,
updateQueue,
null,
prevState,
null,
renderExpirationTime,
);
Expand Down Expand Up @@ -720,7 +719,7 @@ module.exports = function<T, P, I, TI, PI, C, CX, PL>(
function memoizeState(workInProgress: Fiber, nextState: any) {
workInProgress.memoizedState = nextState;
// Don't reset the updateQueue, in case there are pending updates. Resetting
// is handled by beginUpdateQueue.
// is handled by processUpdateQueue.
}

function beginWork(
Expand Down
67 changes: 45 additions & 22 deletions src/renderers/shared/fiber/ReactFiberClassComponent.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,8 @@ var {
isContextConsumer,
} = require('ReactFiberContext');
var {
addUpdate,
addReplaceUpdate,
addForceUpdate,
beginUpdateQueue,
insertUpdateIntoFiber,
processUpdateQueue,
} = require('ReactFiberUpdateQueue');
var {hasContextChanged} = require('ReactFiberContext');
var {isMounted} = require('ReactFiberTreeReflection');
Expand Down Expand Up @@ -77,8 +75,8 @@ if (__DEV__) {
}

module.exports = function(
scheduleUpdate: (fiber: Fiber, expirationTime: ExpirationTime) => void,
getExpirationTime: (fiber: Fiber, forceAsync: boolean) => ExpirationTime,
scheduleWork: (fiber: Fiber, expirationTime: ExpirationTime) => void,
computeExpirationForFiber: (fiber: Fiber) => ExpirationTime,
memoizeProps: (workInProgress: Fiber, props: any) => void,
memoizeState: (workInProgress: Fiber, state: any) => void,
) {
Expand All @@ -87,33 +85,60 @@ module.exports = function(
isMounted,
enqueueSetState(instance, partialState, callback) {
const fiber = ReactInstanceMap.get(instance);
const expirationTime = getExpirationTime(fiber, false);
callback = callback === undefined ? null : callback;
if (__DEV__) {
warnOnInvalidCallback(callback, 'setState');
}
addUpdate(fiber, partialState, callback, expirationTime);
scheduleUpdate(fiber, expirationTime);
const expirationTime = computeExpirationForFiber(fiber);
const update = {
expirationTime,
partialState,
callback,
isReplace: false,
isForced: false,
nextCallback: null,
next: null,
};
insertUpdateIntoFiber(fiber, update);
scheduleWork(fiber, expirationTime);
},
enqueueReplaceState(instance, state, callback) {
const fiber = ReactInstanceMap.get(instance);
const expirationTime = getExpirationTime(fiber, false);
callback = callback === undefined ? null : callback;
if (__DEV__) {
warnOnInvalidCallback(callback, 'replaceState');
}
addReplaceUpdate(fiber, state, callback, expirationTime);
scheduleUpdate(fiber, expirationTime);
const expirationTime = computeExpirationForFiber(fiber);
const update = {
expirationTime,
partialState: state,
callback,
isReplace: true,
isForced: false,
nextCallback: null,
next: null,
};
insertUpdateIntoFiber(fiber, update);
scheduleWork(fiber, expirationTime);
},
enqueueForceUpdate(instance, callback) {
const fiber = ReactInstanceMap.get(instance);
const expirationTime = getExpirationTime(fiber, false);
callback = callback === undefined ? null : callback;
if (__DEV__) {
warnOnInvalidCallback(callback, 'forceUpdate');
}
addForceUpdate(fiber, callback, expirationTime);
scheduleUpdate(fiber, expirationTime);
const expirationTime = computeExpirationForFiber(fiber);
const update = {
expirationTime,
partialState: null,
callback,
isReplace: false,
isForced: true,
nextCallback: null,
next: null,
};
insertUpdateIntoFiber(fiber, update);
scheduleWork(fiber, expirationTime);
},
};

Expand Down Expand Up @@ -404,7 +429,7 @@ module.exports = function(
const unmaskedContext = getUnmaskedContext(workInProgress);

instance.props = props;
instance.state = state;
instance.state = workInProgress.memoizedState = state;
instance.refs = emptyObject;
instance.context = getMaskedContext(workInProgress, unmaskedContext);

Expand All @@ -423,12 +448,11 @@ module.exports = function(
// process them now.
const updateQueue = workInProgress.updateQueue;
if (updateQueue !== null) {
instance.state = beginUpdateQueue(
instance.state = processUpdateQueue(
current,
workInProgress,
updateQueue,
instance,
state,
props,
renderExpirationTime,
);
Expand Down Expand Up @@ -481,7 +505,7 @@ module.exports = function(
// // Process the update queue before calling shouldComponentUpdate
// const updateQueue = workInProgress.updateQueue;
// if (updateQueue !== null) {
// newState = beginUpdateQueue(
// newState = processUpdateQueue(
// workInProgress,
// updateQueue,
// instance,
Expand Down Expand Up @@ -524,7 +548,7 @@ module.exports = function(
// // componentWillMount may have called setState. Process the update queue.
// const newUpdateQueue = workInProgress.updateQueue;
// if (newUpdateQueue !== null) {
// newState = beginUpdateQueue(
// newState = processUpdateQueue(
// workInProgress,
// newUpdateQueue,
// instance,
Expand Down Expand Up @@ -590,12 +614,11 @@ module.exports = function(
// TODO: Previous state can be null.
let newState;
if (workInProgress.updateQueue !== null) {
newState = beginUpdateQueue(
newState = processUpdateQueue(
current,
workInProgress,
workInProgress.updateQueue,
instance,
oldState,
newProps,
renderExpirationTime,
);
Expand Down
21 changes: 8 additions & 13 deletions src/renderers/shared/fiber/ReactFiberCommitWork.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,7 @@ var {
clearCaughtError,
} = require('ReactErrorUtils');

var {
Placement,
Update,
Callback,
ContentReset,
} = require('ReactTypeOfSideEffect');
var {Placement, Update, ContentReset} = require('ReactTypeOfSideEffect');

var invariant = require('fbjs/lib/invariant');

Expand Down Expand Up @@ -132,19 +127,19 @@ module.exports = function<T, P, I, TI, PI, C, CX, PL>(
}
}
}
if (
finishedWork.effectTag & Callback &&
finishedWork.updateQueue !== null
) {
commitCallbacks(finishedWork, finishedWork.updateQueue, instance);
const updateQueue = finishedWork.updateQueue;
if (updateQueue !== null) {
commitCallbacks(updateQueue, instance);
}
return;
}
case HostRoot: {
const updateQueue = finishedWork.updateQueue;
if (updateQueue !== null) {
const instance = finishedWork.child && finishedWork.child.stateNode;
commitCallbacks(finishedWork, updateQueue, instance);
const instance = finishedWork.child !== null
? finishedWork.child.stateNode
: null;
commitCallbacks(updateQueue, instance);
}
return;
}
Expand Down
17 changes: 5 additions & 12 deletions src/renderers/shared/fiber/ReactFiberExpirationTime.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,27 +34,20 @@ function msToExpirationTime(ms: number): ExpirationTime {
exports.msToExpirationTime = msToExpirationTime;

function ceiling(num: number, precision: number): number {
return (((((num * precision) | 0) + 1) / precision) | 0) + 1;
return (((num / precision) | 0) + 1) * precision;
}

function bucket(
function computeExpirationBucket(
currentTime: ExpirationTime,
expirationInMs: number,
precisionInMs: number,
bucketSizeMs: number,
): ExpirationTime {
return ceiling(
currentTime + expirationInMs / UNIT_SIZE,
precisionInMs / UNIT_SIZE,
bucketSizeMs / UNIT_SIZE,
);
}

// Given the current clock time, returns an expiration time. We use rounding
// to batch like updates together.
function asyncExpirationTime(currentTime: ExpirationTime) {
// Should complete within ~1000ms. 1200ms max.
return bucket(currentTime, 1000, 200);
}
exports.asyncExpirationTime = asyncExpirationTime;
exports.computeExpirationBucket = computeExpirationBucket;

// Given the current clock time and an expiration time, returns the
// relative expiration time. Possible values include NoWork, Sync, Task, and
Expand Down
Loading