Skip to content

Commit

Permalink
[Float][Fiber] Implement waitForCommitToBeReady for stylesheet resour…
Browse files Browse the repository at this point in the history
…ces (#26450)

Before a commit is finished if any new stylesheet resources are going to
mount and we are capable of delaying the commit we will do the following

1. Wait for all preloads for newly created stylesheet resources to load
2. Once all preloads are finished we insert the stylesheet instances for
these resources and wait for them all to load
3. Once all stylesheets have loaded we complete the commit

In this PR I also removed the synchronous loadingstate tracking in the
fizz runtime. It was not necessary to support the implementation on not
used by the fizz runtime itself. It makes the inline script slightly
smaller

In this PR I also integrated ReactDOMFloatClient with
ReactDOMHostConfig. It leads to better code factoring, something I
already did on the server a while back. To make the diff a little easier
to follow i make these changes in a single commit so you can look at the
change after that commit if helpful

There is a 500ms timeout which will finish the commit even if all
suspended host instances have not finished loading yet

At the moment error and load events are treated the same and we're
really tracking whether the host instance is finished attempting to
load.

DiffTrain build for [73b6435](73b6435)
  • Loading branch information
gnoff committed Mar 25, 2023
1 parent 1c9327e commit 390ac45
Show file tree
Hide file tree
Showing 25 changed files with 73,225 additions and 69,555 deletions.
2 changes: 1 addition & 1 deletion compiled/facebook-www/REVISION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
909c6dacfde06b87fa22f2e8506c47124cf982b5
73b6435ca4e0c3ae3aac8126509a82420a84f0d7
2 changes: 1 addition & 1 deletion compiled/facebook-www/React-dev.modern.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ if (
}
"use strict";

var ReactVersion = "18.3.0-www-modern-e3dcc95d";
var ReactVersion = "18.3.0-www-modern-11df97fe";

// ATTENTION
// When adding new symbols to this file,
Expand Down
140 changes: 117 additions & 23 deletions compiled/facebook-www/ReactART-dev.classic.js
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ function _assertThisInitialized(self) {
return self;
}

var ReactVersion = "18.3.0-www-classic-7d98bb40";
var ReactVersion = "18.3.0-www-classic-dfeb256e";

var LegacyRoot = 0;
var ConcurrentRoot = 1;
Expand Down Expand Up @@ -2521,33 +2521,44 @@ function lanesToEventPriority(lanes) {

// Renderers that don't support hydration
// can re-export everything from this module.
function shim$1() {
function shim$2() {
throw new Error(
"The current renderer does not support hydration. " +
"This error is likely caused by a bug in React. " +
"Please file an issue."
);
} // Hydration (when unsupported)
var isSuspenseInstancePending = shim$1;
var isSuspenseInstanceFallback = shim$1;
var getSuspenseInstanceFallbackErrorDetails = shim$1;
var registerSuspenseInstanceRetry = shim$1;
var clearSuspenseBoundary = shim$1;
var clearSuspenseBoundaryFromContainer = shim$1;
var errorHydratingContainer = shim$1;
var isSuspenseInstancePending = shim$2;
var isSuspenseInstanceFallback = shim$2;
var getSuspenseInstanceFallbackErrorDetails = shim$2;
var registerSuspenseInstanceRetry = shim$2;
var clearSuspenseBoundary = shim$2;
var clearSuspenseBoundaryFromContainer = shim$2;
var errorHydratingContainer = shim$2;

// Renderers that don't support React Scopes
// can re-export everything from this module.
function shim() {
function shim$1() {
throw new Error(
"The current renderer does not support React Scopes. " +
"This error is likely caused by a bug in React. " +
"Please file an issue."
);
} // React Scopes (when unsupported)

var prepareScopeUpdate = shim;
var getInstanceFromScope = shim;
var prepareScopeUpdate = shim$1;
var getInstanceFromScope = shim$1;

// Renderers that don't support hydration
// can re-export everything from this module.
function shim() {
throw new Error(
"The current renderer does not support Resources. " +
"This error is likely caused by a bug in React. " +
"Please file an issue."
);
} // Resources (when unsupported)
var suspendResource = shim;

var pooledTransform = new Transform();
var NO_CONTEXT = {};
Expand Down Expand Up @@ -2923,6 +2934,9 @@ function unhideTextInstance(textInstance, text) {
function getInstanceFromNode(node) {
throw new Error("Not implemented.");
}
function maySuspendCommit(type, props) {
return false;
}
function preloadInstance(type, props) {
// Return true to indicate it's already loaded
return true;
Expand Down Expand Up @@ -5576,6 +5590,13 @@ function trackUsedThenable(thenableState, thenable, index) {
}
}
}
function suspendCommit() {
// This extra indirection only exists so it can handle passing
// noopSuspenseyCommitThenable through to throwException.
// TODO: Factor the thenable check out of throwException
suspendedThenable = noopSuspenseyCommitThenable;
throw SuspenseyCommitException;
} // This is used to track the actual thenable that suspended so it can be
// passed to the rest of the Suspense implementation — which, for historical
// reasons, expects to receive a thenable.

Expand Down Expand Up @@ -17687,16 +17708,28 @@ function preloadInstanceAndSuspendIfNeeded(
props,
renderLanes
) {
// Ask the renderer if this instance should suspend the commit.
{
// If this flag was set previously, we can remove it. The flag represents
// whether this particular set of props might ever need to suspend. The
// safest thing to do is for maySuspendCommit to always return true, but
// if the renderer is reasonably confident that the underlying resource
// won't be evicted, it can return false as a performance optimization.
workInProgress.flags &= ~SuspenseyCommit;
return;
} // Mark this fiber with a flag. We use this right before the commit phase to
workInProgress.flags |= SuspenseyCommit; // Check if we're rendering at a "non-urgent" priority. This is the same
// check that `useDeferredValue` does to determine whether it needs to
// defer. This is partly for gradual adoption purposes (i.e. shouldn't start
// suspending until you opt in with startTransition or Suspense) but it
// also happens to be the desired behavior for the concrete use cases we've
// thought of so far, like CSS loading, fonts, images, etc.
// TODO: We may decide to expose a way to force a fallback even during a
// sync update.

if (!includesOnlyNonUrgentLanes(renderLanes));
else {
// Preload the instance
var isReady = preloadInstance();

if (!isReady) {
if (shouldRemainOnPreviousScreen());
else {
// Trigger a fallback rather than block the render.
suspendCommit();
}
}
}
}

function scheduleRetryEffect(workInProgress, retryQueue) {
Expand Down Expand Up @@ -18129,6 +18162,8 @@ function completeWork(current, workInProgress, renderLanes) {
popHostContext(workInProgress);
var _type = workInProgress.type;

var _maySuspend = maySuspendCommit();

if (current !== null && workInProgress.stateNode != null) {
updateHostComponent(current, workInProgress, _type, newProps);

Expand Down Expand Up @@ -18183,7 +18218,17 @@ function completeWork(current, workInProgress, renderLanes) {
// will resume rendering as if the work-in-progress completed. So it must
// fully complete.

preloadInstanceAndSuspendIfNeeded(workInProgress);
if (_maySuspend) {
preloadInstanceAndSuspendIfNeeded(
workInProgress,
_type,
newProps,
renderLanes
);
} else {
workInProgress.flags &= ~SuspenseyCommit;
}

return null;
}

Expand Down Expand Up @@ -22559,6 +22604,50 @@ function commitPassiveUnmountEffects(finishedWork) {
commitPassiveUnmountOnFiber(finishedWork);
resetCurrentFiber();
}
function accumulateSuspenseyCommit(finishedWork) {
accumulateSuspenseyCommitOnFiber(finishedWork);
}

function recursivelyAccumulateSuspenseyCommit(parentFiber) {
if (parentFiber.subtreeFlags & SuspenseyCommit) {
var child = parentFiber.child;

while (child !== null) {
accumulateSuspenseyCommitOnFiber(child);
child = child.sibling;
}
}
}

function accumulateSuspenseyCommitOnFiber(fiber) {
switch (fiber.tag) {
case HostHoistable: {
recursivelyAccumulateSuspenseyCommit(fiber);

if (fiber.flags & SuspenseyCommit) {
if (fiber.memoizedState !== null) {
suspendResource();
}
}

break;
}

case HostComponent: {
recursivelyAccumulateSuspenseyCommit(fiber);

break;
}

case HostRoot:
case HostPortal:
// eslint-disable-next-line-no-fallthrough

default: {
recursivelyAccumulateSuspenseyCommit(fiber);
}
}
}

function detachAlternateSiblings(parentFiber) {
// A fiber was deleted from this parent fiber, but it's still part of the
Expand Down Expand Up @@ -24161,6 +24250,11 @@ function commitRootWhenReady(
lanes
) {
if (includesOnlyNonUrgentLanes(lanes)) {
// the suspensey resources. The renderer is responsible for accumulating
// all the load events. This all happens in a single synchronous
// transaction, so it track state in its own module scope.

accumulateSuspenseyCommit(finishedWork); // At the end, ask the renderer if it's ready to commit, or if we should
// suspend. If it's not ready, it will return a callback to subscribe to
// a ready event.

Expand Down
Loading

0 comments on commit 390ac45

Please sign in to comment.