Skip to content
Merged
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