Skip to content

Commit 4fdeaa0

Browse files
committed
Use callback priority to determine cancellation
1 parent bf6990a commit 4fdeaa0

File tree

3 files changed

+104
-27
lines changed

3 files changed

+104
-27
lines changed

packages/react-reconciler/src/ReactFiberWorkLoop.new.js

Lines changed: 51 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -712,33 +712,68 @@ function ensureRootIsScheduled(root: FiberRoot, currentTime: number) {
712712
// This returns the priority level computed during the `getNextLanes` call.
713713
const newCallbackPriority = returnNextLanesPriority();
714714

715-
if (nextLanes === NoLanes) {
716-
// Special case: There's nothing to work on.
717-
if (existingCallbackNode !== null) {
718-
cancelCallback(existingCallbackNode);
715+
if (enableDiscreteEventMicroTasks) {
716+
if (nextLanes === NoLanes) {
717+
// Special case: There's nothing to work on.
718+
if (existingCallbackNode !== null) {
719+
cancelCallback(existingCallbackNode);
720+
}
719721
root.callbackNode = null;
720722
root.callbackPriority = NoLanePriority;
723+
return;
721724
}
722-
return;
723-
}
724725

725-
// Check if there's an existing task. We may be able to reuse it.
726-
if (existingCallbackNode !== null) {
726+
// Check if there's an existing task. We may be able to reuse it.
727727
const existingCallbackPriority = root.callbackPriority;
728-
if (existingCallbackPriority === newCallbackPriority) {
729-
// The priority hasn't changed. We can reuse the existing task. Exit.
728+
if (
729+
existingCallbackPriority !== NoLanePriority &&
730+
existingCallbackPriority !== InputDiscreteLanePriority
731+
) {
732+
if (existingCallbackPriority === newCallbackPriority) {
733+
// The priority hasn't changed. We can reuse the existing task. Exit.
734+
return;
735+
}
736+
737+
invariant(
738+
existingCallbackNode != null,
739+
'Expected scheduled callback to exist. This error is likely caused by a bug in React. Please file an issue.',
740+
);
741+
742+
// The priority changed. Cancel the existing callback. We'll schedule a new
743+
// one below.
744+
cancelCallback(existingCallbackNode);
745+
}
746+
} else {
747+
if (nextLanes === NoLanes) {
748+
// Special case: There's nothing to work on.
749+
if (existingCallbackNode !== null) {
750+
cancelCallback(existingCallbackNode);
751+
root.callbackNode = null;
752+
root.callbackPriority = NoLanePriority;
753+
}
730754
return;
731755
}
732-
// The priority changed. Cancel the existing callback. We'll schedule a new
733-
// one below.
734-
cancelCallback(existingCallbackNode);
756+
757+
// Check if there's an existing task. We may be able to reuse it.
758+
if (existingCallbackNode !== null) {
759+
const existingCallbackPriority = root.callbackPriority;
760+
if (existingCallbackPriority === newCallbackPriority) {
761+
// The priority hasn't changed. We can reuse the existing task. Exit.
762+
return;
763+
}
764+
// The priority changed. Cancel the existing callback. We'll schedule a new
765+
// one below.
766+
cancelCallback(existingCallbackNode);
767+
}
735768
}
736769

737770
// Schedule a new callback.
738771
let newCallbackNode;
739772
if (newCallbackPriority === SyncLanePriority) {
740773
// Special case: Sync React callbacks are scheduled on a special
741774
// internal queue
775+
776+
// TODO: After enableDiscreteEventMicroTasks lands, we can remove the fake node.
742777
newCallbackNode = scheduleSyncCallback(
743778
performSyncWorkOnRoot.bind(null, root),
744779
);
@@ -1879,6 +1914,9 @@ function commitRootImpl(root, renderPriorityLevel) {
18791914
// commitRoot never returns a continuation; it always finishes synchronously.
18801915
// So we can clear these now to allow a new callback to be scheduled.
18811916
root.callbackNode = null;
1917+
if (enableDiscreteEventMicroTasks) {
1918+
root.callbackPriority = NoLanePriority;
1919+
}
18821920

18831921
// Update the first and last pending times on this root. The new first
18841922
// pending time is whatever is left on the root fiber.

packages/react-reconciler/src/ReactFiberWorkLoop.old.js

Lines changed: 51 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -694,33 +694,68 @@ function ensureRootIsScheduled(root: FiberRoot, currentTime: number) {
694694
// This returns the priority level computed during the `getNextLanes` call.
695695
const newCallbackPriority = returnNextLanesPriority();
696696

697-
if (nextLanes === NoLanes) {
698-
// Special case: There's nothing to work on.
699-
if (existingCallbackNode !== null) {
700-
cancelCallback(existingCallbackNode);
697+
if (enableDiscreteEventMicroTasks) {
698+
if (nextLanes === NoLanes) {
699+
// Special case: There's nothing to work on.
700+
if (existingCallbackNode !== null) {
701+
cancelCallback(existingCallbackNode);
702+
}
701703
root.callbackNode = null;
702704
root.callbackPriority = NoLanePriority;
705+
return;
703706
}
704-
return;
705-
}
706707

707-
// Check if there's an existing task. We may be able to reuse it.
708-
if (existingCallbackNode !== null) {
708+
// Check if there's an existing task. We may be able to reuse it.
709709
const existingCallbackPriority = root.callbackPriority;
710-
if (existingCallbackPriority === newCallbackPriority) {
711-
// The priority hasn't changed. We can reuse the existing task. Exit.
710+
if (
711+
existingCallbackPriority !== NoLanePriority &&
712+
existingCallbackPriority !== InputDiscreteLanePriority
713+
) {
714+
if (existingCallbackPriority === newCallbackPriority) {
715+
// The priority hasn't changed. We can reuse the existing task. Exit.
716+
return;
717+
}
718+
719+
invariant(
720+
existingCallbackNode != null,
721+
'Expected scheduled callback to exist. This error is likely caused by a bug in React. Please file an issue.',
722+
);
723+
724+
// The priority changed. Cancel the existing callback. We'll schedule a new
725+
// one below.
726+
cancelCallback(existingCallbackNode);
727+
}
728+
} else {
729+
if (nextLanes === NoLanes) {
730+
// Special case: There's nothing to work on.
731+
if (existingCallbackNode !== null) {
732+
cancelCallback(existingCallbackNode);
733+
root.callbackNode = null;
734+
root.callbackPriority = NoLanePriority;
735+
}
712736
return;
713737
}
714-
// The priority changed. Cancel the existing callback. We'll schedule a new
715-
// one below.
716-
cancelCallback(existingCallbackNode);
738+
739+
// Check if there's an existing task. We may be able to reuse it.
740+
if (existingCallbackNode !== null) {
741+
const existingCallbackPriority = root.callbackPriority;
742+
if (existingCallbackPriority === newCallbackPriority) {
743+
// The priority hasn't changed. We can reuse the existing task. Exit.
744+
return;
745+
}
746+
// The priority changed. Cancel the existing callback. We'll schedule a new
747+
// one below.
748+
cancelCallback(existingCallbackNode);
749+
}
717750
}
718751

719752
// Schedule a new callback.
720753
let newCallbackNode;
721754
if (newCallbackPriority === SyncLanePriority) {
722755
// Special case: Sync React callbacks are scheduled on a special
723756
// internal queue
757+
758+
// TODO: After enableDiscreteEventMicroTasks lands, we can remove the fake node.
724759
newCallbackNode = scheduleSyncCallback(
725760
performSyncWorkOnRoot.bind(null, root),
726761
);
@@ -1859,6 +1894,9 @@ function commitRootImpl(root, renderPriorityLevel) {
18591894
// commitRoot never returns a continuation; it always finishes synchronously.
18601895
// So we can clear these now to allow a new callback to be scheduled.
18611896
root.callbackNode = null;
1897+
if (enableDiscreteEventMicroTasks) {
1898+
root.callbackPriority = NoLanePriority;
1899+
}
18621900

18631901
// Update the first and last pending times on this root. The new first
18641902
// pending time is whatever is left on the root fiber.

scripts/error-codes/codes.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -372,5 +372,6 @@
372372
"381": "This feature is not supported by ReactSuspenseTestUtils.",
373373
"382": "This query has received more parameters than the last time the same query was used. Always pass the exact number of parameters that the query needs.",
374374
"383": "This query has received fewer parameters than the last time the same query was used. Always pass the exact number of parameters that the query needs.",
375-
"384": "Refreshing the cache is not supported in Server Components."
375+
"384": "Refreshing the cache is not supported in Server Components.",
376+
"385": "Expected scheduled callback to exist. This error is likely caused by a bug in React. Please file an issue."
376377
}

0 commit comments

Comments
 (0)