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

double invoke test #1

Closed
wants to merge 44 commits into from
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
fecc288
[react devtools] Device storage support (#25452)
rbalicki2 Oct 25, 2022
1720405
[Float] fix coordination of resource identity and hydration (#25569)
gnoff Oct 27, 2022
09def59
[Float] handle noscript context for Resources (#25559)
gnoff Oct 27, 2022
28a574e
Try assigning fetch to globalThis if global assignment fails (#25571)
sebmarkbage Oct 27, 2022
cf3932b
Remove old react-fetch, react-fs and react-pg libraries (#25577)
sebmarkbage Oct 27, 2022
d2a0176
Detect and warn if use(promise) is wrapped with try/catch block (#25543)
acdlite Oct 28, 2022
5450dd4
Strict Mode: Reuse memoized result from first pass (#25583)
acdlite Oct 28, 2022
d2c0ab1
In work loop, add enum of reasons for suspending
acdlite Oct 24, 2022
952dfff
Split suspended work loop logic into separate functions
acdlite Oct 24, 2022
bdd3d08
Extract logic for detecting bad fallback to helper
acdlite Oct 24, 2022
2ac77aa
Clean up vestige of useOpaqueIdentifier (#25587)
acdlite Oct 30, 2022
765805b
Fix type check for null (#25595)
sebmarkbage Oct 31, 2022
ab075a2
Do not unmount layout effects on initial Offscreen mount (#25592)
sammy-SC Nov 1, 2022
6883d79
[ServerRenderer] Setup for adding data attributes streaming format (#…
mofeiZ Nov 1, 2022
36426e6
Allow uncached IO to stablize (#25561)
acdlite Nov 1, 2022
5f7ef8c
[Float] handle resource Resource creation inside svg context (#25599)
gnoff Nov 1, 2022
8e69bc4
Make host context use null as empty and only error in dev (#25609)
sebmarkbage Nov 1, 2022
4ea063b
refactor isHostResourceType to not receive the context from reconcile…
gnoff Nov 2, 2022
1a90262
Unwrap sync resolved thenables without suspending (#25615)
acdlite Nov 2, 2022
1a08f14
[ServerRenderer] Move fizz external runtime implementation to react-d…
mofeiZ Nov 3, 2022
df61e70
Remove check in renderDidSuspendDelayIfPossible (#25630)
acdlite Nov 3, 2022
4bd245e
Do not unmount layout effects if ancestor Offscreen is hidden (#25628)
sammy-SC Nov 4, 2022
18dff79
[DevTools] add support for HostSingleton & HostResource (#25616)
mondaychen Nov 7, 2022
1e3e30d
Fix useSyncExternalStore dropped update when state is dispatched in r…
pandaiolo Nov 8, 2022
d1e35c7
Don't disappear layout effects unnecessarily (#25660)
sammy-SC Nov 10, 2022
c54e354
[DevTools] bug fix for Hydrating fibers (#25663)
mondaychen Nov 11, 2022
e1dd0a2
Remove recoverable error when a sync update flows into a dehydrated b…
sebmarkbage Nov 16, 2022
c343f80
[react-float] feature detect getRootNode (#25689)
kassens Nov 16, 2022
355dd7d
Bump loader-utils from 2.0.0 to 2.0.4 in /fixtures/flight (#25694)
dependabot[bot] Nov 16, 2022
db8a3fc
Bump loader-utils from 1.4.0 to 1.4.2 in /fixtures/fizz (#25680)
dependabot[bot] Nov 16, 2022
d65b88d
Eagerly initialize an mutable object for instance.refs (#25696)
sebmarkbage Nov 16, 2022
07f46ec
Turn on key spread warning in jsx-runtime for everyone (#25697)
sebmarkbage Nov 16, 2022
6fb8133
Turn on string ref deprecation warning for everybody (not codemoddabl…
eps1lon Nov 17, 2022
7b17f7b
Enable warning for defaultProps on function components for everyone (…
sebmarkbage Nov 17, 2022
44c4e6f
Force unwind work loop during selective hydration (#25695)
acdlite Nov 17, 2022
9dfbd9f
use: Don't suspend if there are pending updates
acdlite Nov 3, 2022
4a2d86b
Don't reset work loop until stack is unwound
acdlite Nov 2, 2022
5eb78d0
Pass ThenableState to replaySuspendedUnitOfWork
acdlite Nov 2, 2022
4387d75
Allow more hooks to be added when replaying mount
acdlite Nov 3, 2022
33e3d28
Reuse hooks when replaying a suspended component
acdlite Nov 3, 2022
6b4c031
Check thenable instead of thenableState
acdlite Nov 3, 2022
f284d9f
Track ThenableState alongside other hooks
acdlite Nov 3, 2022
f31005d
Add SyncHydrationLane (#25698)
tyao1 Nov 17, 2022
b0a6f66
Test for double invoke effect of useLayoutEffect
kassens Nov 17, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Allow uncached IO to stablize (facebook#25561)
Initial draft. I need to test this more.

If a promise is passed to `use`, but the I/O isn't cached, we should
still be able to unwrap it.

This already worked in Server Components, and during SSR.

For Fiber (in the browser), before this fix the state would get lost
between attempts unless the promise resolved immediately in a microtask,
which requires IO to be cached. This was due to an implementation quirk
of Fiber where the state is reset as soon as the stack unwinds. The
workaround is to suspend the entire Fiber work loop until the promise
resolves.

The Server Components and SSR runtimes don't require a workaround: they
can maintain multiple parallel child tasks and reuse the state
indefinitely across attempts. That's ideally how Fiber should work, too,
but it will require larger refactor.

The downside of our approach in Fiber is that it won't "warm up" the
siblings while you're suspended, but to avoid waterfalls you're supposed
to hoist data fetches higher in the tree regardless. But we have other
ideas for how we can add this back in the future. (Though again, this
doesn't affect Server Components, which already have the ideal
behavior.)
  • Loading branch information
acdlite authored Nov 1, 2022
commit 36426e6cb6b6998cba934d239ba6274c9119118c
130 changes: 115 additions & 15 deletions packages/react-reconciler/src/ReactFiberWorkLoop.new.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,13 @@

import {REACT_STRICT_MODE_TYPE} from 'shared/ReactSymbols';

import type {Wakeable} from 'shared/ReactTypes';
import type {Wakeable, Thenable} from 'shared/ReactTypes';
import type {Fiber, FiberRoot} from './ReactInternalTypes';
import type {Lanes, Lane} from './ReactFiberLane.new';
import type {SuspenseState} from './ReactFiberSuspenseComponent.new';
import type {
SuspenseProps,
SuspenseState,
} from './ReactFiberSuspenseComponent.new';
import type {FunctionComponentUpdateQueue} from './ReactFiberHooks.new';
import type {EventPriority} from './ReactEventPriorities.new';
import type {
Expand Down Expand Up @@ -271,6 +274,10 @@ import {
isThenableStateResolved,
} from './ReactFiberThenable.new';
import {schedulePostPaintCallback} from './ReactPostPaintCallback';
import {
getSuspenseHandler,
isBadSuspenseFallback,
} from './ReactFiberSuspenseContext.new';

const ceil = Math.ceil;

Expand Down Expand Up @@ -312,7 +319,7 @@ let workInProgressRootRenderLanes: Lanes = NoLanes;
opaque type SuspendedReason = 0 | 1 | 2 | 3 | 4;
const NotSuspended: SuspendedReason = 0;
const SuspendedOnError: SuspendedReason = 1;
// const SuspendedOnData: SuspendedReason = 2;
const SuspendedOnData: SuspendedReason = 2;
const SuspendedOnImmediate: SuspendedReason = 3;
const SuspendedAndReadyToUnwind: SuspendedReason = 4;

Expand Down Expand Up @@ -706,6 +713,18 @@ export function scheduleUpdateOnFiber(
}
}

// Check if the work loop is currently suspended and waiting for data to
// finish loading.
if (
workInProgressSuspendedReason === SuspendedOnData &&
root === workInProgressRoot
) {
// The incoming update might unblock the current render. Interrupt the
// current attempt and restart from the top.
prepareFreshStack(root, NoLanes);
markRootSuspended(root, workInProgressRootRenderLanes);
}

// Mark that the root has a pending update.
markRootUpdated(root, lane, eventTime);

Expand Down Expand Up @@ -1130,6 +1149,20 @@ function performConcurrentWorkOnRoot(root, didTimeout) {
if (root.callbackNode === originalCallbackNode) {
// The task node scheduled for this root is the same one that's
// currently executed. Need to return a continuation.
if (
workInProgressSuspendedReason === SuspendedOnData &&
workInProgressRoot === root
) {
// Special case: The work loop is currently suspended and waiting for
// data to resolve. Unschedule the current task.
//
// TODO: The factoring is a little weird. Arguably this should be checked
// in ensureRootIsScheduled instead. I went back and forth, not totally
// sure yet.
root.callbackPriority = NoLane;
root.callbackNode = null;
return null;
}
return performConcurrentWorkOnRoot.bind(null, root);
}
return null;
Expand Down Expand Up @@ -1739,7 +1772,9 @@ function handleThrow(root, thrownValue): void {
// deprecate the old API in favor of `use`.
thrownValue = getSuspendedThenable();
workInProgressSuspendedThenableState = getThenableStateAfterSuspending();
workInProgressSuspendedReason = SuspendedOnImmediate;
workInProgressSuspendedReason = shouldAttemptToSuspendUntilDataResolves()
? SuspendedOnData
: SuspendedOnImmediate;
} else {
// This is a regular error. If something earlier in the component already
// suspended, we must clear the thenable state to unblock the work loop.
Expand Down Expand Up @@ -1796,6 +1831,48 @@ function handleThrow(root, thrownValue): void {
}
}

function shouldAttemptToSuspendUntilDataResolves() {
// TODO: We should be able to move the
// renderDidSuspend/renderDidSuspendWithDelay logic into this function,
// instead of repeating it in the complete phase. Or something to that effect.

if (includesOnlyRetries(workInProgressRootRenderLanes)) {
// We can always wait during a retry.
return true;
}

// TODO: We should be able to remove the equivalent check in
// finishConcurrentRender, and rely just on this one.
if (includesOnlyTransitions(workInProgressRootRenderLanes)) {
const suspenseHandler = getSuspenseHandler();
if (suspenseHandler !== null && suspenseHandler.tag === SuspenseComponent) {
const currentSuspenseHandler = suspenseHandler.alternate;
const nextProps: SuspenseProps = suspenseHandler.memoizedProps;
if (isBadSuspenseFallback(currentSuspenseHandler, nextProps)) {
// The nearest Suspense boundary is already showing content. We should
// avoid replacing it with a fallback, and instead wait until the
// data finishes loading.
return true;
} else {
// This is not a bad fallback condition. We should show a fallback
// immediately instead of waiting for the data to resolve. This includes
// when suspending inside new trees.
return false;
}
}

// During a transition, if there is no Suspense boundary (i.e. suspending in
// the "shell" of an application), or if we're inside a hidden tree, then
// we should wait until the data finishes loading.
return true;
}

// For all other Lanes besides Transitions and Retries, we should not wait
// for the data to load.
// TODO: We should wait during Offscreen prerendering, too.
return false;
}

function pushDispatcher(container) {
prepareRendererToRender(container);
const prevDispatcher = ReactCurrentDispatcher.current;
Expand Down Expand Up @@ -2060,7 +2137,7 @@ function renderRootConcurrent(root: FiberRoot, lanes: Lanes) {
markRenderStarted(lanes);
}

do {
outer: do {
try {
if (
workInProgressSuspendedReason !== NotSuspended &&
Expand All @@ -2070,19 +2147,48 @@ function renderRootConcurrent(root: FiberRoot, lanes: Lanes) {
// replay the suspended component.
const unitOfWork = workInProgress;
const thrownValue = workInProgressThrownValue;
workInProgressSuspendedReason = NotSuspended;
workInProgressThrownValue = null;
switch (workInProgressSuspendedReason) {
case SuspendedOnError: {
// Unwind then continue with the normal work loop.
workInProgressSuspendedReason = NotSuspended;
workInProgressThrownValue = null;
unwindSuspendedUnitOfWork(unitOfWork, thrownValue);
break;
}
case SuspendedOnData: {
const didResolve =
workInProgressSuspendedThenableState !== null &&
isThenableStateResolved(workInProgressSuspendedThenableState);
if (didResolve) {
workInProgressSuspendedReason = NotSuspended;
workInProgressThrownValue = null;
replaySuspendedUnitOfWork(unitOfWork, thrownValue);
} else {
// The work loop is suspended on data. We should wait for it to
// resolve before continuing to render.
const thenable: Thenable<mixed> = (workInProgressThrownValue: any);
const onResolution = () => {
ensureRootIsScheduled(root, now());
};
thenable.then(onResolution, onResolution);
break outer;
}
break;
}
case SuspendedOnImmediate: {
// If this fiber just suspended, it's possible the data is already
// cached. Yield to the main thread to give it a chance to ping. If
// it does, we can retry immediately without unwinding the stack.
workInProgressSuspendedReason = SuspendedAndReadyToUnwind;
break outer;
}
default: {
const wasPinged =
workInProgressSuspendedReason = NotSuspended;
workInProgressThrownValue = null;
const didResolve =
workInProgressSuspendedThenableState !== null &&
isThenableStateResolved(workInProgressSuspendedThenableState);
if (wasPinged) {
if (didResolve) {
replaySuspendedUnitOfWork(unitOfWork, thrownValue);
} else {
unwindSuspendedUnitOfWork(unitOfWork, thrownValue);
Expand All @@ -2096,12 +2202,6 @@ function renderRootConcurrent(root: FiberRoot, lanes: Lanes) {
break;
} catch (thrownValue) {
handleThrow(root, thrownValue);
if (workInProgressSuspendedThenableState !== null) {
// If this fiber just suspended, it's possible the data is already
// cached. Yield to the main thread to give it a chance to ping. If
// it does, we can retry immediately without unwinding the stack.
break;
}
}
} while (true);
resetContextDependencies();
Expand Down
Loading