mirror of
https://github.com/facebook/react.git
synced 2025-11-01 09:12:30 +00:00
[Fiber] Don't schedule class fibers without relevant lifecycles for commit (#9105)
* Don't schedule class fibers without relevant lifecycles for commit * Separate Update and Ref effects * Simplify the exit condition * Add missing annotation * Write conditions differently to avoid an extra check * Inline markUpdateIfNecessary() * Inline markUpdateIfAlreadyInProgress()
This commit is contained in:
@@ -227,22 +227,6 @@ module.exports = function(
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function markUpdate(workInProgress) {
|
||||
workInProgress.effectTag |= Update;
|
||||
}
|
||||
|
||||
function markUpdateIfAlreadyInProgress(current: Fiber | null, workInProgress : Fiber) {
|
||||
// If an update was already in progress, we should schedule an Update
|
||||
// effect even though we're bailing out, so that cWU/cDU are called.
|
||||
if (current !== null) {
|
||||
if (workInProgress.memoizedProps !== current.memoizedProps ||
|
||||
workInProgress.memoizedState !== current.memoizedState) {
|
||||
markUpdate(workInProgress);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function resetInputPointers(workInProgress : Fiber, instance : any) {
|
||||
instance.props = workInProgress.memoizedProps;
|
||||
instance.state = workInProgress.memoizedState;
|
||||
@@ -276,7 +260,6 @@ module.exports = function(
|
||||
|
||||
// Invokes the mount life-cycles on a previously never rendered instance.
|
||||
function mountClassInstance(workInProgress : Fiber, priorityLevel : PriorityLevel) : void {
|
||||
markUpdate(workInProgress);
|
||||
const instance = workInProgress.stateNode;
|
||||
const state = instance.state || null;
|
||||
|
||||
@@ -310,12 +293,14 @@ module.exports = function(
|
||||
);
|
||||
}
|
||||
}
|
||||
if (typeof instance.componentDidMount === 'function') {
|
||||
workInProgress.effectTag |= Update;
|
||||
}
|
||||
}
|
||||
|
||||
// Called on a preexisting class instance. Returns false if a resumed render
|
||||
// could be reused.
|
||||
function resumeMountClassInstance(workInProgress : Fiber, priorityLevel : PriorityLevel) : boolean {
|
||||
markUpdate(workInProgress);
|
||||
const instance = workInProgress.stateNode;
|
||||
resetInputPointers(workInProgress, instance);
|
||||
|
||||
@@ -378,6 +363,9 @@ module.exports = function(
|
||||
priorityLevel
|
||||
);
|
||||
}
|
||||
if (typeof instance.componentDidMount === 'function') {
|
||||
workInProgress.effectTag |= Update;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -447,7 +435,14 @@ module.exports = function(
|
||||
oldState === newState &&
|
||||
!hasContextChanged() &&
|
||||
!(updateQueue !== null && updateQueue.hasForceUpdate)) {
|
||||
markUpdateIfAlreadyInProgress(current, workInProgress);
|
||||
// If an update was already in progress, we should schedule an Update
|
||||
// effect even though we're bailing out, so that cWU/cDU are called.
|
||||
if (typeof instance.componentDidUpdate === 'function') {
|
||||
if (oldProps !== current.memoizedProps ||
|
||||
oldState !== current.memoizedState) {
|
||||
workInProgress.effectTag |= Update;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -461,12 +456,21 @@ module.exports = function(
|
||||
);
|
||||
|
||||
if (shouldUpdate) {
|
||||
markUpdate(workInProgress);
|
||||
if (typeof instance.componentWillUpdate === 'function') {
|
||||
instance.componentWillUpdate(newProps, newState, newContext);
|
||||
}
|
||||
if (typeof instance.componentDidUpdate === 'function') {
|
||||
workInProgress.effectTag |= Update;
|
||||
}
|
||||
} else {
|
||||
markUpdateIfAlreadyInProgress(current, workInProgress);
|
||||
// If an update was already in progress, we should schedule an Update
|
||||
// effect even though we're bailing out, so that cWU/cDU are called.
|
||||
if (typeof instance.componentDidUpdate === 'function') {
|
||||
if (oldProps !== current.memoizedProps ||
|
||||
oldState !== current.memoizedState) {
|
||||
workInProgress.effectTag |= Update;
|
||||
}
|
||||
}
|
||||
|
||||
// If shouldComponentUpdate returned false, we should still update the
|
||||
// memoized props/state to indicate that this work can be reused.
|
||||
|
||||
@@ -87,16 +87,6 @@ module.exports = function<T, P, I, TI, PI, C, CX, PL>(
|
||||
}
|
||||
}
|
||||
|
||||
// Only called during update. It's ok to throw.
|
||||
function detachRefIfNeeded(current : Fiber | null, finishedWork : Fiber) {
|
||||
if (current) {
|
||||
const currentRef = current.ref;
|
||||
if (currentRef !== null && currentRef !== finishedWork.ref) {
|
||||
currentRef(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getHostParent(fiber : Fiber) : I | C {
|
||||
let parent = fiber.return;
|
||||
while (parent !== null) {
|
||||
@@ -381,7 +371,6 @@ module.exports = function<T, P, I, TI, PI, C, CX, PL>(
|
||||
function commitWork(current : Fiber | null, finishedWork : Fiber) : void {
|
||||
switch (finishedWork.tag) {
|
||||
case ClassComponent: {
|
||||
detachRefIfNeeded(current, finishedWork);
|
||||
return;
|
||||
}
|
||||
case HostComponent: {
|
||||
@@ -398,7 +387,6 @@ module.exports = function<T, P, I, TI, PI, C, CX, PL>(
|
||||
commitUpdate(instance, updatePayload, type, oldProps, newProps, finishedWork);
|
||||
}
|
||||
}
|
||||
detachRefIfNeeded(current, finishedWork);
|
||||
return;
|
||||
}
|
||||
case HostText: {
|
||||
@@ -435,15 +423,11 @@ module.exports = function<T, P, I, TI, PI, C, CX, PL>(
|
||||
const instance = finishedWork.stateNode;
|
||||
if (finishedWork.effectTag & Update) {
|
||||
if (current === null) {
|
||||
if (typeof instance.componentDidMount === 'function') {
|
||||
instance.componentDidMount();
|
||||
}
|
||||
instance.componentDidMount();
|
||||
} else {
|
||||
if (typeof instance.componentDidUpdate === 'function') {
|
||||
const prevProps = current.memoizedProps;
|
||||
const prevState = current.memoizedState;
|
||||
instance.componentDidUpdate(prevProps, prevState);
|
||||
}
|
||||
const prevProps = current.memoizedProps;
|
||||
const prevState = current.memoizedState;
|
||||
instance.componentDidUpdate(prevProps, prevState);
|
||||
}
|
||||
}
|
||||
if ((finishedWork.effectTag & Callback) && finishedWork.updateQueue !== null) {
|
||||
@@ -495,10 +479,7 @@ module.exports = function<T, P, I, TI, PI, C, CX, PL>(
|
||||
}
|
||||
}
|
||||
|
||||
function commitRef(finishedWork : Fiber) {
|
||||
if (finishedWork.tag !== ClassComponent && finishedWork.tag !== HostComponent) {
|
||||
return;
|
||||
}
|
||||
function commitAttachRef(finishedWork : Fiber) {
|
||||
const ref = finishedWork.ref;
|
||||
if (ref !== null) {
|
||||
const instance = getPublicInstance(finishedWork.stateNode);
|
||||
@@ -506,12 +487,20 @@ module.exports = function<T, P, I, TI, PI, C, CX, PL>(
|
||||
}
|
||||
}
|
||||
|
||||
function commitDetachRef(current : Fiber) {
|
||||
const currentRef = current.ref;
|
||||
if (currentRef !== null) {
|
||||
currentRef(null);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
commitPlacement,
|
||||
commitDeletion,
|
||||
commitWork,
|
||||
commitLifeCycles,
|
||||
commitRef,
|
||||
commitAttachRef,
|
||||
commitDetachRef,
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
@@ -85,6 +85,10 @@ module.exports = function<T, P, I, TI, PI, C, CX, PL>(
|
||||
workInProgress.effectTag |= Update;
|
||||
}
|
||||
|
||||
function markRef(workInProgress : Fiber) {
|
||||
workInProgress.effectTag |= Ref;
|
||||
}
|
||||
|
||||
function appendAllYields(yields : Array<mixed>, workInProgress : Fiber) {
|
||||
let node = workInProgress.stateNode;
|
||||
if (node) {
|
||||
@@ -231,9 +235,12 @@ module.exports = function<T, P, I, TI, PI, C, CX, PL>(
|
||||
workInProgress.updateQueue = (updatePayload : any);
|
||||
// If the update payload indicates that there is a change or if there
|
||||
// is a new ref we mark this as an update.
|
||||
if (updatePayload || current.ref !== workInProgress.ref) {
|
||||
if (updatePayload) {
|
||||
markUpdate(workInProgress);
|
||||
}
|
||||
if (current.ref !== workInProgress.ref) {
|
||||
markRef(workInProgress);
|
||||
}
|
||||
} else {
|
||||
if (!newProps) {
|
||||
invariant(
|
||||
@@ -270,7 +277,7 @@ module.exports = function<T, P, I, TI, PI, C, CX, PL>(
|
||||
workInProgress.stateNode = instance;
|
||||
if (workInProgress.ref !== null) {
|
||||
// If there is a ref on a host node we need to schedule a callback
|
||||
workInProgress.effectTag |= Ref;
|
||||
markRef(workInProgress);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
|
||||
@@ -146,7 +146,8 @@ module.exports = function<T, P, I, TI, PI, C, CX, PL>(config : HostConfig<T, P,
|
||||
commitDeletion,
|
||||
commitWork,
|
||||
commitLifeCycles,
|
||||
commitRef,
|
||||
commitAttachRef,
|
||||
commitDetachRef,
|
||||
} = ReactFiberCommitWork(config, captureError);
|
||||
const {
|
||||
scheduleAnimationCallback: hostScheduleAnimationCallback,
|
||||
@@ -294,16 +295,23 @@ module.exports = function<T, P, I, TI, PI, C, CX, PL>(config : HostConfig<T, P,
|
||||
ReactDebugCurrentFiber.current = nextEffect;
|
||||
}
|
||||
|
||||
if (nextEffect.effectTag & ContentReset) {
|
||||
const effectTag = nextEffect.effectTag;
|
||||
if (effectTag & ContentReset) {
|
||||
config.resetTextContent(nextEffect.stateNode);
|
||||
}
|
||||
|
||||
if (effectTag & Ref) {
|
||||
const current = nextEffect.alternate;
|
||||
if (current !== null) {
|
||||
commitDetachRef(current);
|
||||
}
|
||||
}
|
||||
|
||||
// The following switch statement is only concerned about placement,
|
||||
// updates, and deletions. To avoid needing to add a case for every
|
||||
// possible bitmap value, we remove the secondary effects from the
|
||||
// effect tag and switch on that value.
|
||||
let primaryEffectTag =
|
||||
nextEffect.effectTag & ~(Callback | Err | ContentReset | Ref);
|
||||
let primaryEffectTag = effectTag & ~(Callback | Err | ContentReset | Ref);
|
||||
switch (primaryEffectTag) {
|
||||
case Placement: {
|
||||
commitPlacement(nextEffect);
|
||||
@@ -349,17 +357,19 @@ module.exports = function<T, P, I, TI, PI, C, CX, PL>(config : HostConfig<T, P,
|
||||
|
||||
function commitAllLifeCycles() {
|
||||
while (nextEffect !== null) {
|
||||
const current = nextEffect.alternate;
|
||||
const effectTag = nextEffect.effectTag;
|
||||
|
||||
// Use Task priority for lifecycle updates
|
||||
if (nextEffect.effectTag & (Update | Callback)) {
|
||||
if (effectTag & (Update | Callback)) {
|
||||
const current = nextEffect.alternate;
|
||||
commitLifeCycles(current, nextEffect);
|
||||
}
|
||||
|
||||
if (nextEffect.effectTag & Ref) {
|
||||
commitRef(nextEffect);
|
||||
if (effectTag & Ref) {
|
||||
commitAttachRef(nextEffect);
|
||||
}
|
||||
|
||||
if (nextEffect.effectTag & Err) {
|
||||
if (effectTag & Err) {
|
||||
commitErrorHandling(nextEffect);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user