Skip to content

Commit 6a589ad

Browse files
authored
Add separator comment between text nodes (#21099)
This is needed to avoid mutating the DOM during hydration. This *always* adds it even when it's just text children. We need to avoid this overhead but it's a somewhat tricky problem to solve so we defer the optimization to later.
1 parent 148f8e4 commit 6a589ad

File tree

3 files changed

+19
-8
lines changed

3 files changed

+19
-8
lines changed

packages/react-dom/src/__tests__/ReactDOMFizzServerBrowser-test.js

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ describe('ReactDOMFizzServer', () => {
5555
<div>hello world</div>,
5656
);
5757
const result = await readResult(stream);
58-
expect(result).toBe('<div>hello world</div>');
58+
expect(result).toMatchInlineSnapshot(`"<div>hello world<!-- --></div>"`);
5959
});
6060

6161
// @gate experimental
@@ -93,7 +93,9 @@ describe('ReactDOMFizzServer', () => {
9393
expect(isComplete).toBe(true);
9494

9595
const result = await readResult(stream);
96-
expect(result).toBe('<div><!--$-->Done<!--/$--></div>');
96+
expect(result).toMatchInlineSnapshot(
97+
`"<div><!--$-->Done<!-- --><!--/$--></div>"`,
98+
);
9799
});
98100

99101
// @gate experimental

packages/react-dom/src/__tests__/ReactDOMFizzServerNode-test.js

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,9 @@ describe('ReactDOMFizzServer', () => {
6565
);
6666
startWriting();
6767
jest.runAllTimers();
68-
expect(output.result).toBe('<div>hello world</div>');
68+
expect(output.result).toMatchInlineSnapshot(
69+
`"<div>hello world<!-- --></div>"`,
70+
);
6971
});
7072

7173
// @gate experimental
@@ -81,8 +83,8 @@ describe('ReactDOMFizzServer', () => {
8183
'<!doctype html><html><head><title>test</title><head><body>';
8284
// Then React starts writing.
8385
startWriting();
84-
expect(output.result).toBe(
85-
'<!doctype html><html><head><title>test</title><head><body><div>hello world</div>',
86+
expect(output.result).toMatchInlineSnapshot(
87+
`"<!doctype html><html><head><title>test</title><head><body><div>hello world<!-- --></div>"`,
8688
);
8789
});
8890

@@ -129,8 +131,8 @@ describe('ReactDOMFizzServer', () => {
129131
'<!doctype html><html><head><title>test</title><head><body>';
130132
// Then React starts writing.
131133
startWriting();
132-
expect(output.result).toBe(
133-
'<!doctype html><html><head><title>test</title><head><body><div><!--$-->Done<!--/$--></div>',
134+
expect(output.result).toMatchInlineSnapshot(
135+
`"<!doctype html><html><head><title>test</title><head><body><div><!--$-->Done<!-- --><!--/$--></div>"`,
134136
);
135137
});
136138

packages/react-dom/src/server/ReactDOMServerFormatConfig.js

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,8 @@ export function pushEmpty(
104104
}
105105
}
106106

107+
const textSeparator = stringToPrecomputedChunk('<!-- -->');
108+
107109
export function pushTextInstance(
108110
target: Array<Chunk | PrecomputedChunk>,
109111
text: string,
@@ -113,7 +115,12 @@ export function pushTextInstance(
113115
if (assignID !== null) {
114116
pushDummyNodeWithID(target, responseState, assignID);
115117
}
116-
target.push(stringToChunk(encodeHTMLTextNode(text)));
118+
if (text === '') {
119+
// Empty text doesn't have a DOM node representation and the hydration is aware of this.
120+
return;
121+
}
122+
// TODO: Avoid adding a text separator in common cases.
123+
target.push(stringToChunk(encodeHTMLTextNode(text)), textSeparator);
117124
}
118125

119126
const startTag1 = stringToPrecomputedChunk('<');

0 commit comments

Comments
 (0)