Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
18 changes: 0 additions & 18 deletions packages/react-reconciler/src/ReactFiber.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ import type {ActivityInstance, SuspenseInstance} from './ReactFiberConfig';
import type {
LegacyHiddenProps,
OffscreenProps,
OffscreenInstance,
} from './ReactFiberOffscreenComponent';
import type {ViewTransitionState} from './ReactFiberViewTransitionComponent';
import type {TracingMarkerInstance} from './ReactFiberTracingMarkerComponent';
Expand Down Expand Up @@ -76,7 +75,6 @@ import {
ViewTransitionComponent,
ActivityComponent,
} from './ReactWorkTags';
import {OffscreenVisible} from './ReactFiberOffscreenComponent';
import {getComponentNameFromOwner} from 'react-reconciler/src/getComponentNameFromFiber';
import {isDevToolsPresent} from './ReactFiberDevToolsHook';
import {
Expand Down Expand Up @@ -831,13 +829,6 @@ export function createFiberFromOffscreen(
): Fiber {
const fiber = createFiber(OffscreenComponent, pendingProps, key, mode);
fiber.lanes = lanes;
const primaryChildInstance: OffscreenInstance = {
_visibility: OffscreenVisible,
_pendingMarkers: null,
_retryCache: null,
_transitions: null,
};
fiber.stateNode = primaryChildInstance;
return fiber;
}
export function createFiberFromActivity(
Expand Down Expand Up @@ -885,15 +876,6 @@ export function createFiberFromLegacyHidden(
const fiber = createFiber(LegacyHiddenComponent, pendingProps, key, mode);
fiber.elementType = REACT_LEGACY_HIDDEN_TYPE;
fiber.lanes = lanes;
// Adding a stateNode for legacy hidden because it's currently using
// the offscreen implementation, which depends on a state node
const instance: OffscreenInstance = {
_visibility: OffscreenVisible,
_pendingMarkers: null,
_transitions: null,
_retryCache: null,
};
fiber.stateNode = instance;
return fiber;
}

Expand Down
72 changes: 54 additions & 18 deletions packages/react-reconciler/src/ReactFiberBeginWork.js
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,7 @@ import {
createCapturedValueFromError,
createCapturedValueAtFiber,
} from './ReactCapturedValue';
import {OffscreenVisible} from './ReactFiberOffscreenComponent';
import {
createClassErrorUpdate,
initializeClassErrorUpdate,
Expand Down Expand Up @@ -620,6 +621,18 @@ function updateOffscreenComponent(
const prevState: OffscreenState | null =
current !== null ? current.memoizedState : null;

if (current === null && workInProgress.stateNode === null) {
// We previously reset the work-in-progress.
// We need to create a new Offscreen instance.
const primaryChildInstance: OffscreenInstance = {
_visibility: OffscreenVisible,
_pendingMarkers: null,
_retryCache: null,
_transitions: null,
};
workInProgress.stateNode = primaryChildInstance;
}

if (
nextProps.mode === 'hidden' ||
(enableLegacyHidden && nextProps.mode === 'unstable-defer-without-hiding')
Expand Down Expand Up @@ -788,6 +801,26 @@ function updateOffscreenComponent(
return workInProgress.child;
}

function bailoutOffscreenComponent(
current: Fiber | null,
workInProgress: Fiber,
): Fiber | null {
if (
(current === null || current.tag !== OffscreenComponent) &&
workInProgress.stateNode === null
) {
const primaryChildInstance: OffscreenInstance = {
_visibility: OffscreenVisible,
_pendingMarkers: null,
_retryCache: null,
_transitions: null,
};
workInProgress.stateNode = primaryChildInstance;
}

return workInProgress.sibling;
}

function deferHiddenOffscreenComponent(
current: Fiber | null,
workInProgress: Fiber,
Expand Down Expand Up @@ -1095,9 +1128,13 @@ function updateActivityComponent(
if (nextProps.mode === 'hidden') {
// SSR doesn't render hidden Activity so it shouldn't hydrate,
// even at offscreen lane. Defer to a client rendered offscreen lane.
mountActivityChildren(workInProgress, nextProps, renderLanes);
const primaryChildFragment = mountActivityChildren(
workInProgress,
nextProps,
renderLanes,
);
workInProgress.lanes = laneToLanes(OffscreenLane);
return null;
return bailoutOffscreenComponent(null, primaryChildFragment);
} else {
// We must push the suspense handler context *before* attempting to
// hydrate, to avoid a mismatch in case it errors.
Expand Down Expand Up @@ -2373,7 +2410,7 @@ function updateSuspenseComponent(
if (showFallback) {
pushFallbackTreeSuspenseHandler(workInProgress);

const fallbackFragment = mountSuspenseFallbackChildren(
mountSuspenseFallbackChildren(
workInProgress,
nextPrimaryChildren,
nextFallbackChildren,
Expand Down Expand Up @@ -2408,7 +2445,7 @@ function updateSuspenseComponent(
}
}

return fallbackFragment;
return bailoutOffscreenComponent(null, primaryChildFragment);
} else if (
enableCPUSuspense &&
typeof nextProps.unstable_expectedLoadTime === 'number'
Expand All @@ -2417,7 +2454,7 @@ function updateSuspenseComponent(
// unblock the surrounding content. Then immediately retry after the
// initial commit.
pushFallbackTreeSuspenseHandler(workInProgress);
const fallbackFragment = mountSuspenseFallbackChildren(
mountSuspenseFallbackChildren(
workInProgress,
nextPrimaryChildren,
nextFallbackChildren,
Expand All @@ -2444,7 +2481,7 @@ function updateSuspenseComponent(
// RetryLane even if it's the one currently rendering since we're leaving
// it behind on this node.
workInProgress.lanes = SomeRetryLane;
return fallbackFragment;
return bailoutOffscreenComponent(null, primaryChildFragment);
} else {
pushPrimaryTreeSuspenseHandler(workInProgress);
return mountSuspensePrimaryChildren(
Expand Down Expand Up @@ -2479,7 +2516,7 @@ function updateSuspenseComponent(

const nextFallbackChildren = nextProps.fallback;
const nextPrimaryChildren = nextProps.children;
const fallbackChildFragment = updateSuspenseFallbackChildren(
updateSuspenseFallbackChildren(
current,
workInProgress,
nextPrimaryChildren,
Expand Down Expand Up @@ -2532,7 +2569,7 @@ function updateSuspenseComponent(
renderLanes,
);
workInProgress.memoizedState = SUSPENDED_MARKER;
return fallbackChildFragment;
return bailoutOffscreenComponent(current.child, primaryChildFragment);
} else {
if (
prevState !== null &&
Expand Down Expand Up @@ -2788,7 +2825,7 @@ function updateSuspenseFallbackChildren(
primaryChildFragment.sibling = fallbackChildFragment;
workInProgress.child = primaryChildFragment;

return fallbackChildFragment;
return bailoutOffscreenComponent(null, primaryChildFragment);
}

function retrySuspenseComponentWithoutHydrating(
Expand Down Expand Up @@ -3094,14 +3131,13 @@ function updateDehydratedSuspenseComponent(

const nextPrimaryChildren = nextProps.children;
const nextFallbackChildren = nextProps.fallback;
const fallbackChildFragment =
mountSuspenseFallbackAfterRetryWithoutHydrating(
current,
workInProgress,
nextPrimaryChildren,
nextFallbackChildren,
renderLanes,
);
mountSuspenseFallbackAfterRetryWithoutHydrating(
current,
workInProgress,
nextPrimaryChildren,
nextFallbackChildren,
renderLanes,
);
const primaryChildFragment: Fiber = (workInProgress.child: any);
primaryChildFragment.memoizedState =
mountSuspenseOffscreenState(renderLanes);
Expand All @@ -3111,7 +3147,7 @@ function updateDehydratedSuspenseComponent(
renderLanes,
);
workInProgress.memoizedState = SUSPENDED_MARKER;
return fallbackChildFragment;
return bailoutOffscreenComponent(null, primaryChildFragment);
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4141,4 +4141,28 @@ describe('ReactSuspenseWithNoopRenderer', () => {
</>,
);
});

it('can rerender after resolving a promise', async () => {
const promise = Promise.resolve(null);
const root = ReactNoop.createRoot();

await act(() => {
startTransition(() => {
root.render(<Suspense>{promise}</Suspense>);
});
});

assertLog([]);
expect(root).toMatchRenderedOutput(null);

await act(() => {
startTransition(() => {
root.render(
<Suspense>
<div />
</Suspense>,
);
});
});
});
});
Loading