Skip to content

Commit 238b57f

Browse files
authored
[Blocks] Make it possible to have lazy initialized and lazy loaded Blocks (#18220)
* Lazify Blocks Blocks now initialize lazily. * Initialize Blocks eagerly in ChildFiber This is for the case when it's a new Block that hasn't yet initialized. We need to first initialize it to see what "render function" it resolves to so that we can use that in our comparison. * Remove extra import type line
1 parent 235a6c4 commit 238b57f

5 files changed

Lines changed: 172 additions & 39 deletions

File tree

‎packages/react-reconciler/src/ReactChildFiber.js‎

Lines changed: 36 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
importtype{ReactElement}from'shared/ReactElementType';
1111
importtype{ReactPortal}from'shared/ReactTypes';
12+
importtype{BlockComponent}from'react/src/block';
1213
importtype{Fiber}from'./ReactFiber';
1314
importtype{ExpirationTime}from'./ReactFiberExpirationTime';
1415

@@ -47,6 +48,7 @@ import {
4748
}from'./ReactCurrentFiber';
4849
import{isCompatibleFamilyForHotReloading}from'./ReactFiberHotReloading';
4950
import{StrictMode}from'./ReactTypeOfMode';
51+
import{initializeBlockComponentType}from'shared/ReactLazyComponent';
5052

5153
letdidWarnAboutMaps;
5254
letdidWarnAboutGenerators;
@@ -420,18 +422,25 @@ function ChildReconciler(shouldTrackSideEffects) {
420422
}elseif(
421423
enableBlocksAPI&&
422424
current.tag===Block&&
423-
element.type.$$typeof===REACT_BLOCK_TYPE&&
424-
element.type.render===current.type.render
425+
element.type.$$typeof===REACT_BLOCK_TYPE
425426
){
426-
// Same as above but also update the .type field.
427-
constexisting=useFiber(current,element.props);
428-
existing.return=returnFiber;
429-
existing.type=element.type;
430-
if(__DEV__){
431-
existing._debugSource=element._source;
432-
existing._debugOwner=element._owner;
427+
// The new Block might not be initialized yet. We need to initialize
428+
// it in case initializing it turns out it would match.
429+
initializeBlockComponentType(element.type);
430+
if(
431+
(element.type: BlockComponent<any,any,any>)._fn===
432+
(current.type: BlockComponent<any,any,any>)._fn
433+
){
434+
// Same as above but also update the .type field.
435+
constexisting=useFiber(current,element.props);
436+
existing.return=returnFiber;
437+
existing.type=element.type;
438+
if(__DEV__){
439+
existing._debugSource=element._source;
440+
existing._debugOwner=element._owner;
441+
}
442+
returnexisting;
433443
}
434-
returnexisting;
435444
}
436445
}
437446
// Insert
@@ -1179,19 +1188,24 @@ function ChildReconciler(shouldTrackSideEffects) {
11791188
}
11801189
caseBlock:
11811190
if(enableBlocksAPI){
1182-
if(
1183-
element.type.$$typeof===REACT_BLOCK_TYPE&&
1184-
element.type.render===child.type.render
1185-
){
1186-
deleteRemainingChildren(returnFiber,child.sibling);
1187-
constexisting=useFiber(child,element.props);
1188-
existing.type=element.type;
1189-
existing.return=returnFiber;
1190-
if(__DEV__){
1191-
existing._debugSource=element._source;
1192-
existing._debugOwner=element._owner;
1191+
if(element.type.$$typeof===REACT_BLOCK_TYPE){
1192+
// The new Block might not be initialized yet. We need to initialize
1193+
// it in case initializing it turns out it would match.
1194+
initializeBlockComponentType(element.type);
1195+
if(
1196+
(element.type: BlockComponent<any,any,any>)._fn===
1197+
(child.type: BlockComponent<any,any,any>)._fn
1198+
){
1199+
deleteRemainingChildren(returnFiber,child.sibling);
1200+
constexisting=useFiber(child,element.props);
1201+
existing.type=element.type;
1202+
existing.return=returnFiber;
1203+
if(__DEV__){
1204+
existing._debugSource=element._source;
1205+
existing._debugOwner=element._owner;
1206+
}
1207+
returnexisting;
11931208
}
1194-
returnexisting;
11951209
}
11961210
}
11971211
// We intentionally fallthrough here if enableBlocksAPI is not on.

‎packages/react-reconciler/src/ReactFiberBeginWork.js‎

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
*/
99

1010
importtype{ReactProviderType,ReactContext}from'shared/ReactTypes';
11+
importtype{BlockComponent}from'react/src/block';
1112
importtype{Fiber}from'./ReactFiber';
1213
importtype{FiberRoot}from'./ReactFiberRoot';
1314
importtype{ExpirationTime}from'./ReactFiberExpirationTime';
@@ -167,6 +168,7 @@ import {
167168
readLazyComponentType,
168169
resolveDefaultProps,
169170
}from'./ReactFiberLazyComponent';
171+
import{initializeBlockComponentType}from'shared/ReactLazyComponent';
170172
import{
171173
resolveLazyComponentTag,
172174
createFiberFromTypeAndProps,
@@ -182,6 +184,7 @@ import {
182184
renderDidSuspendDelayIfPossible,
183185
markUnprocessedUpdateTime,
184186
}from'./ReactFiberWorkLoop';
187+
import{Resolved}from'shared/ReactLazyStatusTags';
185188

186189
constReactCurrentOwner=ReactSharedInternals.ReactCurrentOwner;
187190

@@ -700,19 +703,24 @@ function updateFunctionComponent(
700703
returnworkInProgress.child;
701704
}
702705

703-
functionupdateBlock(
706+
functionupdateBlock<Props,Payload,Data>(
704707
current: Fiber|null,
705708
workInProgress: Fiber,
706-
block: any,
709+
block: BlockComponent<Props,Payload,Data>,
707710
nextProps: any,
708711
renderExpirationTime: ExpirationTime,
709712
){
710713
// TODO: current can be non-null here even if the component
711714
// hasn't yet mounted. This happens after the first render suspends.
712715
// We'll need to figure out if this is fine or can cause issues.
713716

714-
constrender=block.render;
715-
constdata=block.query();
717+
initializeBlockComponentType(block);
718+
if(block._status!==Resolved){
719+
throwblock._data;
720+
}
721+
722+
constrender=block._fn;
723+
constdata=block._data;
716724

717725
// The rest is a fork of updateFunctionComponent
718726
letnextChildren;

‎packages/react-reconciler/src/ReactFiberHooks.js‎

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -343,12 +343,12 @@ function areHookInputsEqual(
343343
returntrue;
344344
}
345345

346-
exportfunctionrenderWithHooks(
346+
exportfunctionrenderWithHooks<Props,SecondArg>(
347347
current: Fiber|null,
348348
workInProgress: Fiber,
349-
Component: any,
350-
props: any,
351-
secondArg: any,
349+
Component: (p: Props,arg: SecondArg)=>any,
350+
props: Props,
351+
secondArg: SecondArg,
352352
nextRenderExpirationTime: ExpirationTime,
353353
): any{
354354
renderExpirationTime=nextRenderExpirationTime;

‎packages/react/src/block.js‎

Lines changed: 66 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
*
44
* This source code is licensed under the MIT license found in the
55
* LICENSE file in the root directory of this source tree.
6+
*
7+
* @flow
68
*/
79

810
import{
@@ -11,14 +13,64 @@ import {
1113
REACT_FORWARD_REF_TYPE,
1214
}from'shared/ReactSymbols';
1315

16+
typeBlockQueryFunction<Args: Iterable<any>,Data>=(...args: Args)=>Data;
17+
typeBlockRenderFunction<Props,Data>=(
18+
props: Props,
19+
data: Data,
20+
)=>React$Node;
21+
22+
typeThenable<T,R>={
23+
then(resolve: (T)=>mixed,reject: (mixed)=>mixed): R,
24+
};
25+
26+
typeInitializer<Props,Payload,Data>=(
27+
payload: Payload,
28+
)=>
29+
|[Data,BlockRenderFunction<Props,Data>]
30+
|Thenable<[Data,BlockRenderFunction<Props,Data>],mixed>;
31+
32+
exporttypeUninitializedBlockComponent<Props,Payload,Data>={
33+
$$typeof: Symbol|number,
34+
_status: -1,
35+
_data: Payload,
36+
_fn: Initializer<Props, Payload, Data>,
37+
};
38+
39+
exporttypePendingBlockComponent<Props,Data>={
40+
$$typeof: Symbol|number,
41+
_status: 0,
42+
_data: Thenable<[Data,BlockRenderFunction<Props,Data>],mixed>,
43+
_fn: null,
44+
};
45+
46+
exporttypeResolvedBlockComponent<Props,Data>={
47+
$$typeof: Symbol|number,
48+
_status: 1,
49+
_data: Data,
50+
_fn: BlockRenderFunction<Props, Data>,
51+
};
52+
53+
exporttypeRejectedBlockComponent={
54+
$$typeof: Symbol|number,
55+
_status: 2,
56+
_data: mixed,
57+
_fn: null,
58+
};
59+
60+
exporttypeBlockComponent<Props,Payload,Data>=
61+
|UninitializedBlockComponent<Props,Payload,Data>
62+
|PendingBlockComponent<Props,Data>
63+
|ResolvedBlockComponent<Props,Data>
64+
|RejectedBlockComponent;
65+
1466
opaquetypeBlock<Props>: React$AbstractComponent<
1567
Props,
1668
null,
1769
>=React$AbstractComponent<Props,null>;
1870

19-
exportdefaultfunctionblock<Args,Props,Data>(
20-
query: (...args: Args)=>Data,
21-
render: (props: Props,data: Data)=>React$Node,
71+
exportdefaultfunctionblock<Args: Iterable<any>, Props, Data>(
72+
query: BlockQueryFunction<Args,Data>,
73+
render: BlockRenderFunction<Props,Data>,
2274
): (...args: Args)=>Block<Props>{
2375
if(__DEV__){
2476
if(typeofquery!=='function'){
@@ -63,14 +115,19 @@ export default function block<Args, Props, Data>(
63115
);
64116
}
65117
}
118+
functioninitializer(args){
119+
letdata=query.apply(null,args);
120+
return[data,render];
121+
}
66122
returnfunction(): Block<Props>{
67-
letargs=arguments;
68-
return{
123+
letargs: Args=arguments;
124+
let blockComponent: UninitializedBlockComponent<Props,Args,Data>={
69125
$$typeof: REACT_BLOCK_TYPE,
70-
query: function(){
71-
returnquery.apply(null,args);
72-
},
73-
render: render,
126+
_status: -1,
127+
_data: args,
128+
_fn: initializer,
74129
};
130+
// $FlowFixMe
131+
returnblockComponent;
75132
};
76133
}

‎packages/shared/ReactLazyComponent.js‎

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,13 @@ import type {
1414
LazyComponent,
1515
}from'react/src/ReactLazy';
1616

17+
importtype{
18+
PendingBlockComponent,
19+
ResolvedBlockComponent,
20+
RejectedBlockComponent,
21+
BlockComponent,
22+
}from'react/src/block';
23+
1724
import{
1825
Uninitialized,
1926
Pending,
@@ -74,3 +81,50 @@ export function initializeLazyComponentType(
7481
);
7582
}
7683
}
84+
85+
exportfunctioninitializeBlockComponentType<Props,Payload,Data>(
86+
blockComponent: BlockComponent<Props,Payload,Data>,
87+
): void {
88+
if(blockComponent._status===Uninitialized){
89+
constthenableOrTuple=blockComponent._fn(blockComponent._data);
90+
if(typeofthenableOrTuple.then!=='function'){
91+
lettuple: [any,any]=(thenableOrTuple: any);
92+
constresolved: ResolvedBlockComponent<
93+
Props,
94+
Data,
95+
>=(blockComponent: any);
96+
resolved._status=Resolved;
97+
resolved._data=tuple[0];
98+
resolved._fn=tuple[1];
99+
return;
100+
}
101+
constthenable=(thenableOrTuple: any);
102+
// Transition to the next state.
103+
constpending: PendingBlockComponent<Props,Data>=(blockComponent: any);
104+
pending._status=Pending;
105+
pending._data=thenable;
106+
pending._fn=null;
107+
thenable.then(
108+
(tuple: [any,any])=>{
109+
if(blockComponent._status===Pending){
110+
// Transition to the next state.
111+
constresolved: ResolvedBlockComponent<
112+
Props,
113+
Data,
114+
>=(blockComponent: any);
115+
resolved._status=Resolved;
116+
resolved._data=tuple[0];
117+
resolved._fn=tuple[1];
118+
}
119+
},
120+
error=>{
121+
if(blockComponent._status===Pending){
122+
// Transition to the next state.
123+
constrejected: RejectedBlockComponent=(blockComponent: any);
124+
rejected._status=Rejected;
125+
rejected._data=error;
126+
}
127+
},
128+
);
129+
}
130+
}

0 commit comments

Comments
 (0)