-
Notifications
You must be signed in to change notification settings - Fork 50.3k
[Flight] Resolve deduped references when chunks are blocked on each other #32316
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
Changes from 3 commits
de28a94
48add5b
ecb5a8a
0052b76
438f110
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -163,6 +163,7 @@ type BlockedChunk<T> = { | |||||||||||
| status: 'blocked', | ||||||||||||
| value: null | Array<(T) => mixed>, | ||||||||||||
| reason: null | Array<(mixed) => mixed>, | ||||||||||||
| intermediaryValue?: mixed, | ||||||||||||
| _response: Response, | ||||||||||||
| _children: Array<SomeChunk<any>> | ProfilingResult, // Profiling-only | ||||||||||||
| _debugInfo?: null | ReactDebugInfo, // DEV-only | ||||||||||||
|
|
@@ -568,6 +569,7 @@ type InitializationHandler = { | |||||||||||
| }; | ||||||||||||
| let initializingHandler: null | InitializationHandler = null; | ||||||||||||
| let initializingChunk: null | BlockedChunk<any> = null; | ||||||||||||
| let isParsingRootModel = false; | ||||||||||||
|
|
||||||||||||
| function initializeModelChunk<T>(chunk: ResolvedModelChunk<T>): void { | ||||||||||||
| const prevHandler = initializingHandler; | ||||||||||||
|
|
@@ -584,9 +586,7 @@ function initializeModelChunk<T>(chunk: ResolvedModelChunk<T>): void { | |||||||||||
| cyclicChunk.value = null; | ||||||||||||
| cyclicChunk.reason = null; | ||||||||||||
|
|
||||||||||||
| if (enableProfilerTimer && enableComponentPerformanceTrack) { | ||||||||||||
| initializingChunk = cyclicChunk; | ||||||||||||
| } | ||||||||||||
| initializingChunk = cyclicChunk; | ||||||||||||
|
|
||||||||||||
| try { | ||||||||||||
| const value: T = parseModel(chunk._response, resolvedModel); | ||||||||||||
|
|
@@ -620,9 +620,7 @@ function initializeModelChunk<T>(chunk: ResolvedModelChunk<T>): void { | |||||||||||
| erroredChunk.reason = error; | ||||||||||||
| } finally { | ||||||||||||
| initializingHandler = prevHandler; | ||||||||||||
| if (enableProfilerTimer && enableComponentPerformanceTrack) { | ||||||||||||
| initializingChunk = prevChunk; | ||||||||||||
| } | ||||||||||||
| initializingChunk = prevChunk; | ||||||||||||
| } | ||||||||||||
| } | ||||||||||||
|
|
||||||||||||
|
|
@@ -876,8 +874,18 @@ function createElement( | |||||||||||
| return createLazyChunkWrapper(erroredChunk); | ||||||||||||
| } | ||||||||||||
| if (handler.deps > 0) { | ||||||||||||
| // We have blocked references inside this Element but we can turn this into | ||||||||||||
| // a Lazy node referencing this Element to let everything around it proceed. | ||||||||||||
| // We have blocked references inside this element but we can turn this | ||||||||||||
| // into a Lazy node referencing this element to let everything around it | ||||||||||||
| // proceed. If the element is for the root model, we store it as an | ||||||||||||
| // intermediary value on the initializing chunk, so that referencing | ||||||||||||
| // models can peek into it to resolve their value. | ||||||||||||
| if ( | ||||||||||||
| isParsingRootModel && | ||||||||||||
| initializingChunk && | ||||||||||||
| !initializingChunk.intermediaryValue | ||||||||||||
| ) { | ||||||||||||
| initializingChunk.intermediaryValue = element; | ||||||||||||
| } | ||||||||||||
| const blockedChunk: BlockedChunk<React$Element<any>> = | ||||||||||||
| createBlockedChunk(response); | ||||||||||||
| handler.value = element; | ||||||||||||
|
|
@@ -967,6 +975,15 @@ function waitForReference<T>( | |||||||||||
| } else if (chunk.status === INITIALIZED) { | ||||||||||||
| value = chunk.value; | ||||||||||||
| continue; | ||||||||||||
| } else if ( | ||||||||||||
| // For the first iteration of the path we need the root model of the | ||||||||||||
| // referenced chunk. If we have an intermediary value for it, we can | ||||||||||||
| // peek into it, even if it's not fully resolved yet. | ||||||||||||
| i === 1 && | ||||||||||||
| referencedChunk.status === BLOCKED && | ||||||||||||
| referencedChunk.intermediaryValue | ||||||||||||
| ) { | ||||||||||||
| value = referencedChunk.intermediaryValue; | ||||||||||||
| } else { | ||||||||||||
| // If we're not yet initialized we need to skip what we've already drilled | ||||||||||||
| // through and then wait for the next value to become available. | ||||||||||||
|
|
@@ -1410,13 +1427,11 @@ function parseModelString( | |||||||||||
| // Lazy node | ||||||||||||
| const id = parseInt(value.slice(2), 16); | ||||||||||||
| const chunk = getChunk(response, id); | ||||||||||||
| if (enableProfilerTimer && enableComponentPerformanceTrack) { | ||||||||||||
| if ( | ||||||||||||
| initializingChunk !== null && | ||||||||||||
| isArray(initializingChunk._children) | ||||||||||||
| ) { | ||||||||||||
| initializingChunk._children.push(chunk); | ||||||||||||
| } | ||||||||||||
| if ( | ||||||||||||
| initializingChunk !== null && | ||||||||||||
| isArray(initializingChunk._children) | ||||||||||||
| ) { | ||||||||||||
| initializingChunk._children.push(chunk); | ||||||||||||
| } | ||||||||||||
| // We create a React.lazy wrapper around any lazy values. | ||||||||||||
| // When passed into React, we'll know how to suspend on this. | ||||||||||||
|
|
@@ -1430,13 +1445,11 @@ function parseModelString( | |||||||||||
| } | ||||||||||||
| const id = parseInt(value.slice(2), 16); | ||||||||||||
| const chunk = getChunk(response, id); | ||||||||||||
| if (enableProfilerTimer && enableComponentPerformanceTrack) { | ||||||||||||
| if ( | ||||||||||||
| initializingChunk !== null && | ||||||||||||
| isArray(initializingChunk._children) | ||||||||||||
| ) { | ||||||||||||
| initializingChunk._children.push(chunk); | ||||||||||||
| } | ||||||||||||
| if ( | ||||||||||||
| initializingChunk !== null && | ||||||||||||
| isArray(initializingChunk._children) | ||||||||||||
| ) { | ||||||||||||
| initializingChunk._children.push(chunk); | ||||||||||||
| } | ||||||||||||
| return chunk; | ||||||||||||
| } | ||||||||||||
|
|
@@ -3393,6 +3406,7 @@ function parseModel<T>(response: Response, json: UninitializedModel): T { | |||||||||||
| function createFromJSONCallback(response: Response) { | ||||||||||||
| // $FlowFixMe[missing-this-annot] | ||||||||||||
| return function (key: string, value: JSONValue) { | ||||||||||||
| isParsingRootModel = key === ''; | ||||||||||||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We can't assume that an empty string key is the root since it may also be on nested objects. What's the implication of that?
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hmm, we were already using the check here: react/packages/react-client/src/ReactFlightClient.js Lines 983 to 987 in 192555b
But I guess you're right, it could also be an explicit empty key in some object. |
||||||||||||
| if (typeof value === 'string') { | ||||||||||||
| // We can't use .bind here because we need the "this" value. | ||||||||||||
| return parseModelString(response, this, key, value); | ||||||||||||
|
|
||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is not "Profiling-only" anymore. I'm not sure this is actually reliable enough. It's more of a hack for profiling which is optional if it works or not.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we can keep
_childrenprofiling-only. It was not necessary for this fix to remove the two guards aroundinitializingChunk._children. I reverted that.