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
56 changes: 6 additions & 50 deletions packages/react-reconciler/src/ReactChildFiber.js
Original file line number Diff line number Diff line change
Expand Up @@ -68,9 +68,9 @@ import {
SuspenseActionException,
createThenableState,
trackUsedThenable,
resolveLazy,
} from './ReactFiberThenable';
import {readContextDuringReconciliation} from './ReactFiberNewContext';
import {callLazyInitInDEV} from './ReactFiberCallUserSpace';

import {runWithFiberInDEV} from './ReactCurrentFiber';

Expand Down Expand Up @@ -364,15 +364,6 @@ function warnOnSymbolType(returnFiber: Fiber, invalidChild: symbol) {
}
}

function resolveLazy(lazyType: any) {
if (__DEV__) {
return callLazyInitInDEV(lazyType);
}
const payload = lazyType._payload;
const init = lazyType._init;
return init(payload);
}

type ChildReconciler = (
returnFiber: Fiber,
currentFirstChild: Fiber | null,
Expand Down Expand Up @@ -698,14 +689,7 @@ function createChildReconciler(
}
case REACT_LAZY_TYPE: {
const prevDebugInfo = pushDebugInfo(newChild._debugInfo);
let resolvedChild;
if (__DEV__) {
resolvedChild = callLazyInitInDEV(newChild);
} else {
const payload = newChild._payload;
const init = newChild._init;
resolvedChild = init(payload);
}
const resolvedChild = resolveLazy((newChild: any));
const created = createChild(returnFiber, resolvedChild, lanes);
currentDebugInfo = prevDebugInfo;
return created;
Expand Down Expand Up @@ -830,14 +814,7 @@ function createChildReconciler(
}
case REACT_LAZY_TYPE: {
const prevDebugInfo = pushDebugInfo(newChild._debugInfo);
let resolvedChild;
if (__DEV__) {
resolvedChild = callLazyInitInDEV(newChild);
} else {
const payload = newChild._payload;
const init = newChild._init;
resolvedChild = init(payload);
}
const resolvedChild = resolveLazy((newChild: any));
const updated = updateSlot(
returnFiber,
oldFiber,
Expand Down Expand Up @@ -962,14 +939,7 @@ function createChildReconciler(
}
case REACT_LAZY_TYPE: {
const prevDebugInfo = pushDebugInfo(newChild._debugInfo);
let resolvedChild;
if (__DEV__) {
resolvedChild = callLazyInitInDEV(newChild);
} else {
const payload = newChild._payload;
const init = newChild._init;
resolvedChild = init(payload);
}
const resolvedChild = resolveLazy((newChild: any));
const updated = updateFromMap(
existingChildren,
returnFiber,
Expand Down Expand Up @@ -1086,14 +1056,7 @@ function createChildReconciler(
});
break;
case REACT_LAZY_TYPE: {
let resolvedChild;
if (__DEV__) {
resolvedChild = callLazyInitInDEV((child: any));
} else {
const payload = child._payload;
const init = (child._init: any);
resolvedChild = init(payload);
}
const resolvedChild = resolveLazy((child: any));
warnOnInvalidKey(
returnFiber,
workInProgress,
Expand Down Expand Up @@ -1809,14 +1772,7 @@ function createChildReconciler(
);
case REACT_LAZY_TYPE: {
const prevDebugInfo = pushDebugInfo(newChild._debugInfo);
let result;
if (__DEV__) {
result = callLazyInitInDEV(newChild);
} else {
const payload = newChild._payload;
const init = newChild._init;
result = init(payload);
}
const result = resolveLazy((newChild: any));
const firstChild = reconcileChildFibersImpl(
returnFiber,
currentFirstChild,
Expand Down
16 changes: 3 additions & 13 deletions packages/react-reconciler/src/ReactFiberBeginWork.js
Original file line number Diff line number Diff line change
Expand Up @@ -302,11 +302,8 @@ import {
pushRootMarkerInstance,
TransitionTracingMarker,
} from './ReactFiberTracingMarkerComponent';
import {
callLazyInitInDEV,
callComponentInDEV,
callRenderInDEV,
} from './ReactFiberCallUserSpace';
import {callComponentInDEV, callRenderInDEV} from './ReactFiberCallUserSpace';
import {resolveLazy} from './ReactFiberThenable';

// A special exception that's used to unwind the stack when an update flows
// into a dehydrated boundary.
Expand Down Expand Up @@ -2020,14 +2017,7 @@ function mountLazyComponent(

const props = workInProgress.pendingProps;
const lazyComponent: LazyComponentType<any, any> = elementType;
let Component;
if (__DEV__) {
Component = callLazyInitInDEV(lazyComponent);
} else {
const payload = lazyComponent._payload;
const init = lazyComponent._init;
Component = init(payload);
}
let Component = resolveLazy(lazyComponent);
// Store the unwrapped component in the type.
workInProgress.type = Component;

Expand Down
25 changes: 25 additions & 0 deletions packages/react-reconciler/src/ReactFiberThenable.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ import type {
RejectedThenable,
} from 'shared/ReactTypes';

import type {LazyComponent as LazyComponentType} from 'react/src/ReactLazy';

import {callLazyInitInDEV} from './ReactFiberCallUserSpace';

import {getWorkInProgressRoot} from './ReactFiberWorkLoop';

import ReactSharedInternals from 'shared/ReactSharedInternals';
Expand Down Expand Up @@ -260,6 +264,27 @@ export function suspendCommit(): void {
throw SuspenseyCommitException;
}

export function resolveLazy<T>(lazyType: LazyComponentType<T, any>): T {
try {
if (__DEV__) {
return callLazyInitInDEV(lazyType);
}
const payload = lazyType._payload;
const init = lazyType._init;
return init(payload);
} catch (x) {
if (x !== null && typeof x === 'object' && typeof x.then === 'function') {
// This lazy Suspended. Treat this as if we called use() to unwrap it.
suspendedThenable = x;
if (__DEV__) {
needsToResetSuspendedThenableDEV = true;
}
throw SuspenseException;
}
throw x;
}
}

// 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
17 changes: 3 additions & 14 deletions packages/react-reconciler/src/__tests__/ReactLazy-test.internal.js
Original file line number Diff line number Diff line change
Expand Up @@ -198,10 +198,7 @@ describe('ReactLazy', () => {

await resolveFakeImport(Foo);

await waitForAll([
'Foo',
...(gate('alwaysThrottleRetries') ? [] : ['Foo']),
]);
await waitForAll(['Foo']);
expect(root).not.toMatchRenderedOutput('FooBar');

await act(() => resolveFakeImport(Bar));
Expand Down Expand Up @@ -1329,11 +1326,7 @@ describe('ReactLazy', () => {
expect(ref.current).toBe(null);

await act(() => resolveFakeImport(Foo));
assertLog([
'Foo',
// pre-warming
'Foo',
]);
assertLog(['Foo']);
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now a Lazy gets to participate in the optimization when it can be immediately resolved in place instead of causing a rerender.

This doesn't have any of the other legacy quirks like infinite pinging concerns since these are unconditionally rendered in the child position and never inside a component render.


await act(() => resolveFakeImport(ForwardRefBar));
assertLog(['Foo', 'forwardRef', 'Bar']);
Expand Down Expand Up @@ -1493,11 +1486,7 @@ describe('ReactLazy', () => {
expect(root).not.toMatchRenderedOutput('AB');

await act(() => resolveFakeImport(ChildA));
assertLog([
'A',
// pre-warming
'A',
]);
assertLog(['A']);

await act(() => resolveFakeImport(ChildB));
assertLog(['A', 'B', 'Did mount: A', 'Did mount: B']);
Expand Down
Loading