From d47339ea3659133940f7699144cd30453fb6ed43 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Markb=C3=A5ge?= Date: Fri, 24 Sep 2021 10:22:02 -0400 Subject: [PATCH] [Fizz] Remove assignID mechanism (#22410) * Remove pushEmpty This is only used to support the assignID mechanism. * Remove assignID mechanism This effectively isn't used anyway because we always insert a dummy tag into the fallback. * Emit the template tag with an ID directly in pending boundaries This ensures that assigning the ID is deterministic since it's done during writing. This also avoids emitting it for client rendered boundaries that start as client rendered since we never need to refer to them. * Move lazy ID initialization to the core implementation We never need an ID before we write a pending boundary. This also ensures that ID generation is deterministic by moving it to the write phase. * Simplify the inserted scripts We can assume that there are no text nodes before the template tag so this simplifies the script that finds the comment node. It should be the direct previous child. --- .../src/server/ReactDOMServerFormatConfig.js | 230 ++++-------------- .../ReactDOMServerLegacyFormatConfig.js | 12 +- .../server/ReactNativeServerFormatConfig.js | 15 +- .../src/ReactNoopServer.js | 4 +- packages/react-server/src/ReactFizzServer.js | 85 ++----- .../forks/ReactServerFormatConfig.custom.js | 5 +- 6 files changed, 73 insertions(+), 278 deletions(-) diff --git a/packages/react-dom/src/server/ReactDOMServerFormatConfig.js b/packages/react-dom/src/server/ReactDOMServerFormatConfig.js index 1b2bbc28f6669..78e909cff610c 100644 --- a/packages/react-dom/src/server/ReactDOMServerFormatConfig.js +++ b/packages/react-dom/src/server/ReactDOMServerFormatConfig.js @@ -168,17 +168,17 @@ export function getChildFormatContext( return parentContext; } -// This object is used to lazily reuse the ID of the first generated node, or assign one. -// We can't assign an ID up front because the node we're attaching it to might already -// have one. So we need to lazily use that if it's available. -export type SuspenseBoundaryID = { - formattedID: null | PrecomputedChunk, -}; +export type SuspenseBoundaryID = null | PrecomputedChunk; + +export const UNINITIALIZED_SUSPENSE_BOUNDARY_ID: SuspenseBoundaryID = null; -export function createSuspenseBoundaryID( +export function assignSuspenseBoundaryID( responseState: ResponseState, ): SuspenseBoundaryID { - return {formattedID: null}; + const generatedID = responseState.nextSuspenseID++; + return stringToPrecomputedChunk( + responseState.boundaryPrefix + generatedID.toString(16), + ); } export type OpaqueIDType = string; @@ -201,50 +201,13 @@ function encodeHTMLTextNode(text: string): string { return escapeTextForBrowser(text); } -function assignAnID( - responseState: ResponseState, - id: SuspenseBoundaryID, -): PrecomputedChunk { - // TODO: This approach doesn't yield deterministic results since this is assigned during render. - const generatedID = responseState.nextSuspenseID++; - return (id.formattedID = stringToPrecomputedChunk( - responseState.boundaryPrefix + generatedID.toString(16), - )); -} - -const dummyNode1 = stringToPrecomputedChunk(''); - -function pushDummyNodeWithID( - target: Array, - responseState: ResponseState, - assignID: SuspenseBoundaryID, -): void { - const id = assignAnID(responseState, assignID); - target.push(dummyNode1, id, dummyNode2); -} - -export function pushEmpty( - target: Array, - responseState: ResponseState, - assignID: null | SuspenseBoundaryID, -): void { - if (assignID !== null) { - pushDummyNodeWithID(target, responseState, assignID); - } -} - const textSeparator = stringToPrecomputedChunk(''); export function pushTextInstance( target: Array, text: string, responseState: ResponseState, - assignID: null | SuspenseBoundaryID, ): void { - if (assignID !== null) { - pushDummyNodeWithID(target, responseState, assignID); - } if (text === '') { // Empty text doesn't have a DOM node representation and the hydration is aware of this. return; @@ -514,30 +477,6 @@ function pushAttribute( const endOfStartTag = stringToPrecomputedChunk('>'); const endOfStartTagSelfClosing = stringToPrecomputedChunk('/>'); -const idAttr = stringToPrecomputedChunk(' id="'); -const attrEnd = stringToPrecomputedChunk('"'); - -function pushID( - target: Array, - responseState: ResponseState, - assignID: SuspenseBoundaryID, - existingID: mixed, -): void { - if ( - existingID !== null && - existingID !== undefined && - (typeof existingID === 'string' || typeof existingID === 'object') - ) { - // We can reuse the existing ID for our purposes. - assignID.formattedID = stringToPrecomputedChunk( - escapeTextForBrowser(existingID), - ); - } else { - const encodedID = assignAnID(responseState, assignID); - target.push(idAttr, encodedID, attrEnd); - } -} - function pushInnerHTML( target: Array, innerHTML, @@ -598,7 +537,6 @@ function pushStartSelect( target: Array, props: Object, responseState: ResponseState, - assignID: null | SuspenseBoundaryID, ): ReactNodeList { if (__DEV__) { checkControlledValueProps('select', props); @@ -651,9 +589,6 @@ function pushStartSelect( } } } - if (assignID !== null) { - pushID(target, responseState, assignID, props.id); - } target.push(endOfStartTag); pushInnerHTML(target, innerHTML, children); @@ -693,7 +628,6 @@ function pushStartOption( props: Object, responseState: ResponseState, formatContext: FormatContext, - assignID: null | SuspenseBoundaryID, ): ReactNodeList { const selectedValue = formatContext.selectedValue; @@ -776,10 +710,6 @@ function pushStartOption( target.push(selectedMarkerAttribute); } - if (assignID !== null) { - pushID(target, responseState, assignID, props.id); - } - target.push(endOfStartTag); pushInnerHTML(target, innerHTML, children); return children; @@ -789,7 +719,6 @@ function pushInput( target: Array, props: Object, responseState: ResponseState, - assignID: null | SuspenseBoundaryID, ): ReactNodeList { if (__DEV__) { checkControlledValueProps('input', props); @@ -883,10 +812,6 @@ function pushInput( pushAttribute(target, responseState, 'value', defaultValue); } - if (assignID !== null) { - pushID(target, responseState, assignID, props.id); - } - target.push(endOfStartTagSelfClosing); return null; } @@ -895,7 +820,6 @@ function pushStartTextArea( target: Array, props: Object, responseState: ResponseState, - assignID: null | SuspenseBoundaryID, ): ReactNodeList { if (__DEV__) { checkControlledValueProps('textarea', props); @@ -952,10 +876,6 @@ function pushStartTextArea( value = defaultValue; } - if (assignID !== null) { - pushID(target, responseState, assignID, props.id); - } - target.push(endOfStartTag); // TODO (yungsters): Remove support for children content in