Skip to content

Commit

Permalink
Emit reactroot attribute on the first element we discover (#21154)
Browse files Browse the repository at this point in the history
This may not be the first root element if the root is a fragment and the
second one unsuspends first. But this tag doesn't work well for root
fragments anyway.
  • Loading branch information
sebmarkbage committed Apr 14, 2021
1 parent a4a940d commit 266c26a
Show file tree
Hide file tree
Showing 4 changed files with 37 additions and 6 deletions.
4 changes: 4 additions & 0 deletions packages/react-dom/src/__tests__/ReactDOMFizzServer-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,10 @@ describe('ReactDOMFizzServer', () => {
// We assume this is a React added ID that's a non-visual implementation detail.
continue;
}
if (attributes[i].name === 'data-reactroot') {
// We ignore React injected attributes.
continue;
}
props[attributes[i].name] = attributes[i].value;
}
props.children = getVisibleChildren(node);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,9 @@ describe('ReactDOMFizzServer', () => {
<div>hello world</div>,
);
const result = await readResult(stream);
expect(result).toMatchInlineSnapshot(`"<div>hello world<!-- --></div>"`);
expect(result).toMatchInlineSnapshot(
`"<div data-reactroot=\\"\\">hello world<!-- --></div>"`,
);
});

// @gate experimental
Expand Down Expand Up @@ -94,7 +96,7 @@ describe('ReactDOMFizzServer', () => {

const result = await readResult(stream);
expect(result).toMatchInlineSnapshot(
`"<div><!--$-->Done<!-- --><!--/$--></div>"`,
`"<div data-reactroot=\\"\\"><!--$-->Done<!-- --><!--/$--></div>"`,
);
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ describe('ReactDOMFizzServer', () => {
startWriting();
jest.runAllTimers();
expect(output.result).toMatchInlineSnapshot(
`"<div>hello world<!-- --></div>"`,
`"<div data-reactroot=\\"\\">hello world<!-- --></div>"`,
);
});

Expand All @@ -84,7 +84,7 @@ describe('ReactDOMFizzServer', () => {
// Then React starts writing.
startWriting();
expect(output.result).toMatchInlineSnapshot(
`"<!doctype html><html><head><title>test</title><head><body><div>hello world<!-- --></div>"`,
`"<!doctype html><html><head><title>test</title><head><body><div data-reactroot=\\"\\">hello world<!-- --></div>"`,
);
});

Expand Down Expand Up @@ -132,7 +132,7 @@ describe('ReactDOMFizzServer', () => {
// Then React starts writing.
startWriting();
expect(output.result).toMatchInlineSnapshot(
`"<!doctype html><html><head><title>test</title><head><body><div><!--$-->Done<!-- --><!--/$--></div>"`,
`"<!doctype html><html><head><title>test</title><head><body><div data-reactroot=\\"\\"><!--$-->Done<!-- --><!--/$--></div>"`,
);
});

Expand Down
27 changes: 26 additions & 1 deletion packages/react-dom/src/server/ReactDOMServerFormatConfig.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import {
OVERLOADED_BOOLEAN,
NUMERIC,
POSITIVE_NUMERIC,
ROOT_ATTRIBUTE_NAME,
} from '../shared/DOMProperty';
import {isUnitlessNumber} from '../shared/CSSProperty';

Expand Down Expand Up @@ -63,6 +64,7 @@ export type ResponseState = {
sentCompleteSegmentFunction: boolean,
sentCompleteBoundaryFunction: boolean,
sentClientRenderFunction: boolean,
hasEmittedRoot: boolean,
};

// Allows us to keep track of what we've already written so we can refer back to it.
Expand All @@ -79,6 +81,7 @@ export function createResponseState(
sentCompleteSegmentFunction: false,
sentCompleteBoundaryFunction: false,
sentClientRenderFunction: false,
hasEmittedRoot: false,
};
}

Expand All @@ -99,7 +102,7 @@ type InsertionMode = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7;

// Lets us keep track of contextual state and pick it back up after suspending.
export type FormatContext = {
insertionMode: InsertionMode, // root/svg/html/mathml/table
insertionMode: InsertionMode, // svg/html/mathml/table
selectedValue: null | string | Array<string>, // the selected value(s) inside a <select>, or null outside <select>
};

Expand Down Expand Up @@ -508,6 +511,19 @@ const endOfStartTagSelfClosing = stringToPrecomputedChunk('/>');
const idAttr = stringToPrecomputedChunk(' id="');
const attrEnd = stringToPrecomputedChunk('"');

const reactRootAttribute = stringToPrecomputedChunk(
' ' + ROOT_ATTRIBUTE_NAME + '=""',
);
function pushReactRoot(
target: Array<Chunk | PrecomputedChunk>,
responseState: ResponseState,
): void {
if (!responseState.hasEmittedRoot) {
responseState.hasEmittedRoot = true;
target.push(reactRootAttribute);
}
}

function pushID(
target: Array<Chunk | PrecomputedChunk>,
responseState: ResponseState,
Expand Down Expand Up @@ -639,6 +655,7 @@ function pushStartSelect(
if (assignID !== null) {
pushID(target, responseState, assignID, props.id);
}
pushReactRoot(target, responseState);

target.push(endOfStartTag);
pushInnerHTML(target, innerHTML, children);
Expand Down Expand Up @@ -752,6 +769,7 @@ function pushStartOption(
if (assignID !== null) {
pushID(target, responseState, assignID, props.id);
}
pushReactRoot(target, responseState);

target.push(endOfStartTag);
return children;
Expand Down Expand Up @@ -839,6 +857,7 @@ function pushInput(
if (assignID !== null) {
pushID(target, responseState, assignID, props.id);
}
pushReactRoot(target, responseState);

target.push(endOfStartTagSelfClosing);
return null;
Expand Down Expand Up @@ -903,6 +922,7 @@ function pushStartTextArea(
if (assignID !== null) {
pushID(target, responseState, assignID, props.id);
}
pushReactRoot(target, responseState);

target.push(endOfStartTag);

Expand Down Expand Up @@ -979,6 +999,7 @@ function pushSelfClosing(
if (assignID !== null) {
pushID(target, responseState, assignID, props.id);
}
pushReactRoot(target, responseState);

target.push(endOfStartTagSelfClosing);
return null;
Expand Down Expand Up @@ -1015,6 +1036,7 @@ function pushStartMenuItem(
if (assignID !== null) {
pushID(target, responseState, assignID, props.id);
}
pushReactRoot(target, responseState);

target.push(endOfStartTag);
return null;
Expand Down Expand Up @@ -1053,6 +1075,7 @@ function pushStartGenericElement(
if (assignID !== null) {
pushID(target, responseState, assignID, props.id);
}
pushReactRoot(target, responseState);

target.push(endOfStartTag);
pushInnerHTML(target, innerHTML, children);
Expand Down Expand Up @@ -1111,6 +1134,7 @@ function pushStartCustomElement(
if (assignID !== null) {
pushID(target, responseState, assignID, props.id);
}
pushReactRoot(target, responseState);

target.push(endOfStartTag);
pushInnerHTML(target, innerHTML, children);
Expand Down Expand Up @@ -1152,6 +1176,7 @@ function pushStartPreformattedElement(
if (assignID !== null) {
pushID(target, responseState, assignID, props.id);
}
pushReactRoot(target, responseState);

target.push(endOfStartTag);

Expand Down

0 comments on commit 266c26a

Please sign in to comment.