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

Decouple update queue from Fiber type #12600

Merged
merged 10 commits into from
Apr 23, 2018
Prev Previous commit
Next Next commit
Optimize for class components
Change `process` and `callback` to match the expected payload types
for class components. I had intended for the update queue to be reusable
for both class components and a future React API, but we'll likely have
to fork anyway.
  • Loading branch information
acdlite committed Apr 21, 2018
commit 5be461c02bc21f9e31fa55a34729b90812168fe1
15 changes: 12 additions & 3 deletions packages/react-reconciler/src/ReactFiberBeginWork.js
Original file line number Diff line number Diff line change
Expand Up @@ -411,9 +411,18 @@ export default function<T, P, I, TI, HI, PI, C, CC, CX, PL>(
pushHostRootContext(workInProgress);
let updateQueue = workInProgress.updateQueue;
if (updateQueue !== null) {
const prevChildren = workInProgress.memoizedState;
processUpdateQueue(workInProgress, updateQueue, renderExpirationTime);
const nextChildren = workInProgress.memoizedState;
const nextProps = workInProgress.pendingProps;
const prevState = workInProgress.memoizedState;
const prevChildren = prevState !== null ? prevState.children : null;
processUpdateQueue(
workInProgress,
updateQueue,
nextProps,
null,
renderExpirationTime,
);
const nextState = workInProgress.memoizedState;
const nextChildren = nextState.children;

if (nextChildren === prevChildren) {
// If the state is the same as before, that's a bailout because we had
Expand Down
120 changes: 39 additions & 81 deletions packages/react-reconciler/src/ReactFiberClassComponent.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ import {
enqueueUpdate,
processUpdateQueue,
createUpdate,
ReplaceState,
ForceUpdate,
} from './ReactUpdateQueue';
import {NoWork} from './ReactFiberExpirationTime';

Expand Down Expand Up @@ -160,60 +162,19 @@ export default function(
hasContextChanged,
} = legacyContext;

function callCallback(callback, context) {
invariant(
typeof callback === 'function',
'Invalid argument passed as callback. Expected a function. Instead ' +
'received: %s',
callback,
);
callback.call(context);
}

const classComponentUpdater = {
isMounted,
enqueueSetState(inst, payload, callback) {
const fiber = ReactInstanceMap.get(inst);
const expirationTime = computeExpirationForFiber(fiber);

const update = createUpdate(expirationTime);
update.process = (workInProgress, prevState) => {
let partialState;
if (typeof payload === 'function') {
// Updater function
const instance = workInProgress.stateNode;
const nextProps = workInProgress.pendingProps;

if (
debugRenderPhaseSideEffects ||
(debugRenderPhaseSideEffectsForStrictMode &&
workInProgress.mode & StrictMode)
) {
// Invoke the updater an extra time to help detect side-effects.
payload.call(instance, prevState, nextProps);
}

partialState = payload.call(instance, prevState, nextProps);
} else {
// Partial state object
partialState = payload;
}
if (partialState === null || partialState === undefined) {
// Null and undefined are treated as no-ops.
return prevState;
}
// Merge the partial state and the previous state.
return Object.assign({}, prevState, partialState);
};

update.payload = payload;
if (callback !== undefined && callback !== null) {
if (__DEV__) {
warnOnInvalidCallback(callback, 'setState');
}
update.commit = finishedWork => {
const instance = finishedWork.stateNode;
callCallback(callback, instance);
};
update.callback = callback;
}

enqueueUpdate(fiber, update, expirationTime);
Expand All @@ -224,35 +185,14 @@ export default function(
const expirationTime = computeExpirationForFiber(fiber);

const update = createUpdate(expirationTime);
update.process = (workInProgress, prevState) => {
if (typeof payload === 'function') {
// Updater function
const instance = workInProgress.stateNode;
const nextProps = workInProgress.pendingProps;

if (
debugRenderPhaseSideEffects ||
(debugRenderPhaseSideEffectsForStrictMode &&
workInProgress.mode & StrictMode)
) {
// Invoke the updater an extra time to help detect side-effects.
payload.call(instance, prevState, nextProps);
}

return payload.call(instance, prevState, nextProps);
}
// State object
return payload;
};
update.tag = ReplaceState;
update.payload = payload;

if (callback !== undefined && callback !== null) {
if (__DEV__) {
warnOnInvalidCallback(callback, 'setState');
warnOnInvalidCallback(callback, 'replaceState');
}
update.commit = finishedWork => {
const instance = finishedWork.stateNode;
callCallback(callback, instance);
};
update.callback = callback;
}

enqueueUpdate(fiber, update, expirationTime);
Expand All @@ -263,19 +203,13 @@ export default function(
const expirationTime = computeExpirationForFiber(fiber);

const update = createUpdate(expirationTime);
update.process = (workInProgress, prevState, queue) => {
queue.hasForceUpdate = true;
return prevState;
};
update.tag = ForceUpdate;

if (callback !== undefined && callback !== null) {
if (__DEV__) {
warnOnInvalidCallback(callback, 'setState');
warnOnInvalidCallback(callback, 'forceUpdate');
}
update.commit = finishedWork => {
const instance = finishedWork.stateNode;
callCallback(callback, instance);
};
update.callback = callback;
}

enqueueUpdate(fiber, update, expirationTime);
Expand Down Expand Up @@ -752,7 +686,13 @@ export default function(

let updateQueue = workInProgress.updateQueue;
if (updateQueue !== null) {
processUpdateQueue(workInProgress, updateQueue, renderExpirationTime);
processUpdateQueue(
workInProgress,
updateQueue,
props,
instance,
renderExpirationTime,
);
instance.state = workInProgress.memoizedState;
}

Expand Down Expand Up @@ -780,7 +720,13 @@ export default function(
// process them now.
updateQueue = workInProgress.updateQueue;
if (updateQueue !== null) {
processUpdateQueue(workInProgress, updateQueue, renderExpirationTime);
processUpdateQueue(
workInProgress,
updateQueue,
props,
instance,
renderExpirationTime,
);
instance.state = workInProgress.memoizedState;
}
}
Expand Down Expand Up @@ -835,7 +781,13 @@ export default function(
let newState = (instance.state = oldState);
let updateQueue = workInProgress.updateQueue;
if (updateQueue !== null) {
processUpdateQueue(workInProgress, updateQueue, renderExpirationTime);
processUpdateQueue(
workInProgress,
updateQueue,
newProps,
instance,
renderExpirationTime,
);
newState = workInProgress.memoizedState;
}

Expand Down Expand Up @@ -963,7 +915,13 @@ export default function(
let newState = (instance.state = oldState);
let updateQueue = workInProgress.updateQueue;
if (updateQueue !== null) {
processUpdateQueue(workInProgress, updateQueue, renderExpirationTime);
processUpdateQueue(
workInProgress,
updateQueue,
newProps,
instance,
renderExpirationTime,
);
newState = workInProgress.memoizedState;
}

Expand Down
27 changes: 25 additions & 2 deletions packages/react-reconciler/src/ReactFiberCommitWork.js
Original file line number Diff line number Diff line change
Expand Up @@ -251,14 +251,37 @@ export default function<T, P, I, TI, HI, PI, C, CC, CX, PL>(
}
const updateQueue = finishedWork.updateQueue;
if (updateQueue !== null) {
commitUpdateQueue(finishedWork, updateQueue, committedExpirationTime);
instance.props = finishedWork.memoizedProps;
instance.state = finishedWork.memoizedState;
commitUpdateQueue(
finishedWork,
updateQueue,
instance,
committedExpirationTime,
);
}
return;
}
case HostRoot: {
const updateQueue = finishedWork.updateQueue;
if (updateQueue !== null) {
commitUpdateQueue(finishedWork, updateQueue, committedExpirationTime);
let instance = null;
if (finishedWork.child !== null) {
switch (finishedWork.child.tag) {
case HostComponent:
instance = getPublicInstance(finishedWork.child.stateNode);
break;
case ClassComponent:
instance = finishedWork.child.stateNode;
break;
}
}
commitUpdateQueue(
finishedWork,
updateQueue,
instance,
committedExpirationTime,
);
}
return;
}
Expand Down
26 changes: 3 additions & 23 deletions packages/react-reconciler/src/ReactFiberReconciler.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import {
findCurrentHostFiberWithNoPortals,
} from 'react-reconciler/reflection';
import * as ReactInstanceMap from 'shared/ReactInstanceMap';
import {ClassComponent, HostComponent} from 'shared/ReactTypeOfWork';
import {HostComponent} from 'shared/ReactTypeOfWork';
import emptyObject from 'fbjs/lib/emptyObject';
import getComponentName from 'shared/getComponentName';
import invariant from 'fbjs/lib/invariant';
Expand Down Expand Up @@ -340,8 +340,7 @@ export default function<T, P, I, TI, HI, PI, C, CC, CX, PL>(
}

const update = createUpdate(expirationTime);

update.process = () => element;
update.payload = {children: element};

callback = callback === undefined ? null : callback;
if (callback !== null) {
Expand All @@ -351,26 +350,7 @@ export default function<T, P, I, TI, HI, PI, C, CC, CX, PL>(
'function. Instead received: %s.',
callback,
);
update.commit = finishedWork => {
let instance = null;
if (finishedWork.child !== null) {
switch (finishedWork.child.tag) {
case HostComponent:
instance = getPublicInstance(finishedWork.child.stateNode);
break;
case ClassComponent:
instance = finishedWork.child.stateNode;
break;
}
}
invariant(
typeof callback === 'function',
'Invalid argument passed as callback. Expected a function. Instead ' +
'received: %s',
callback,
);
callback.call(instance);
};
update.callback = callback;
}
enqueueUpdate(current, update, expirationTime);

Expand Down
16 changes: 12 additions & 4 deletions packages/react-reconciler/src/ReactFiberScheduler.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ import {
Deletion,
ContentReset,
Callback,
ShouldCapture,
DidCapture,
Ref,
Incomplete,
HostEffectMask,
Expand Down Expand Up @@ -803,7 +803,7 @@ export default function<T, P, I, TI, HI, PI, C, CC, CX, PL>(
// capture values if possible.
const next = unwindWork(workInProgress);
// Because this fiber did not complete, don't reset its expiration time.
if (workInProgress.effectTag & ShouldCapture) {
if (workInProgress.effectTag & DidCapture) {
// Restarting an error boundary
stopFailedWorkTimer(workInProgress);
} else {
Expand Down Expand Up @@ -1065,7 +1065,11 @@ export default function<T, P, I, TI, HI, PI, C, CC, CX, PL>(
break;
case HostRoot: {
const errorInfo = createCapturedValue(value, sourceFiber);
const update = createRootErrorUpdate(errorInfo, expirationTime);
const update = createRootErrorUpdate(
fiber,
errorInfo,
expirationTime,
);
enqueueUpdate(fiber, update, expirationTime);
scheduleWork(fiber, expirationTime);
return;
Expand All @@ -1079,7 +1083,11 @@ export default function<T, P, I, TI, HI, PI, C, CC, CX, PL>(
// itself should capture it.
const rootFiber = sourceFiber;
const errorInfo = createCapturedValue(value, rootFiber);
const update = createRootErrorUpdate(errorInfo, expirationTime);
const update = createRootErrorUpdate(
rootFiber,
errorInfo,
expirationTime,
);
enqueueUpdate(rootFiber, update, expirationTime);
scheduleWork(rootFiber, expirationTime);
}
Expand Down
Loading