mirror of
https://github.com/zebrajr/react.git
synced 2025-12-06 12:20:20 +01:00
[Fiber] Treat unwrapping React.lazy more like a use() (#34031)
While we want to get rid of React.lazy's special wrapper type and just use a Promise for the type, we still have the wrapper. However, this is still conceptually the same as a Usable in that it should be have the same if you `use(promise)` or render a Promise as a child or type position. This PR makes it behave like a `use()` when we unwrap them. We could move to a model where it actually reaches the internal of the Lazy's Promise when it unwraps but for now I leave the lazy API signature intact by just catching the Promise and then "use()" that. This lets us align on the semantics with `use()` such as the suspense yield optimization. It also lets us warn or fork based on legacy throw-a-Promise behavior where as `React.lazy` is not deprecated.
This commit is contained in:
parent
b1cbb482d5
commit
9be531cd37
56
packages/react-reconciler/src/ReactChildFiber.js
vendored
56
packages/react-reconciler/src/ReactChildFiber.js
vendored
|
|
@ -68,9 +68,9 @@ import {
|
|||
SuspenseActionException,
|
||||
createThenableState,
|
||||
trackUsedThenable,
|
||||
resolveLazy,
|
||||
} from './ReactFiberThenable';
|
||||
import {readContextDuringReconciliation} from './ReactFiberNewContext';
|
||||
import {callLazyInitInDEV} from './ReactFiberCallUserSpace';
|
||||
|
||||
import {runWithFiberInDEV} from './ReactCurrentFiber';
|
||||
|
||||
|
|
@ -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,
|
||||
|
|
@ -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;
|
||||
|
|
@ -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,
|
||||
|
|
@ -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,
|
||||
|
|
@ -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,
|
||||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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';
|
||||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
|
|
@ -1329,11 +1326,7 @@ describe('ReactLazy', () => {
|
|||
expect(ref.current).toBe(null);
|
||||
|
||||
await act(() => resolveFakeImport(Foo));
|
||||
assertLog([
|
||||
'Foo',
|
||||
// pre-warming
|
||||
'Foo',
|
||||
]);
|
||||
assertLog(['Foo']);
|
||||
|
||||
await act(() => resolveFakeImport(ForwardRefBar));
|
||||
assertLog(['Foo', 'forwardRef', 'Bar']);
|
||||
|
|
@ -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']);
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user