Skip to content

Commit e748104

Browse files
committed
Use template tag for placeholders and inserted dummy nodes with IDs
These can be used in any parent. At least outside IE11. Not sure yet what happens in IE11 to these. Not sure if these are bad for perf since they're special nodes.
1 parent c8b7a94 commit e748104

File tree

2 files changed

+76
-8
lines changed

2 files changed

+76
-8
lines changed

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

Lines changed: 70 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,11 @@ describe('ReactDOMFizzServer', () => {
9797
let node = element.firstChild;
9898
while (node) {
9999
if (node.nodeType === 1) {
100-
if (node.tagName !== 'SCRIPT' && !node.hasAttribute('hidden')) {
100+
if (
101+
node.tagName !== 'SCRIPT' &&
102+
node.tagName !== 'TEMPLATE' &&
103+
!node.hasAttribute('hidden')
104+
) {
101105
const props = {};
102106
const attributes = node.attributes;
103107
for (let i = 0; i < attributes.length; i++) {
@@ -408,4 +412,69 @@ describe('ReactDOMFizzServer', () => {
408412
</div>,
409413
]);
410414
});
415+
416+
// @gate experimental
417+
it('can resolve async content in esoteric parents', async () => {
418+
function AsyncOption({text}) {
419+
return <option>{readText(text)}</option>;
420+
}
421+
422+
function AsyncCol({className}) {
423+
return <col className={readText(className)}>{[]}</col>;
424+
}
425+
426+
function App() {
427+
return (
428+
<div>
429+
<select>
430+
<Suspense fallback="Loading...">
431+
<AsyncOption text="Hello" />
432+
</Suspense>
433+
</select>
434+
<Suspense fallback="Loading...">
435+
<table>
436+
<colgroup>
437+
<AsyncCol className="World" />
438+
</colgroup>
439+
</table>
440+
</Suspense>
441+
</div>
442+
);
443+
}
444+
445+
await act(async () => {
446+
const {startWriting} = ReactDOMFizzServer.pipeToNodeWritable(
447+
<App />,
448+
writable,
449+
);
450+
startWriting();
451+
});
452+
453+
expect(getVisibleChildren(container)).toEqual(
454+
<div>
455+
<select>Loading...</select>Loading...
456+
</div>,
457+
);
458+
459+
await act(async () => {
460+
resolveText('Hello');
461+
});
462+
463+
await act(async () => {
464+
resolveText('World');
465+
});
466+
467+
expect(getVisibleChildren(container)).toEqual(
468+
<div>
469+
<select>
470+
<option>Hello</option>
471+
</select>
472+
<table>
473+
<colgroup>
474+
<col className="World" />
475+
</colgroup>
476+
</table>
477+
</div>,
478+
);
479+
});
411480
});

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

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -154,8 +154,8 @@ function assignAnID(
154154
));
155155
}
156156

157-
const dummyNode1 = stringToPrecomputedChunk('<span hidden id="');
158-
const dummyNode2 = stringToPrecomputedChunk('"></span>');
157+
const dummyNode1 = stringToPrecomputedChunk('<template id="');
158+
const dummyNode2 = stringToPrecomputedChunk('"></template>');
159159

160160
function pushDummyNodeWithID(
161161
target: Array<Chunk | PrecomputedChunk>,
@@ -247,16 +247,15 @@ export function pushEndInstance(
247247
// Structural Nodes
248248

249249
// A placeholder is a node inside a hidden partial tree that can be filled in later, but before
250-
// display. It's never visible to users.
251-
const placeholder1 = stringToPrecomputedChunk('<span id="');
252-
const placeholder2 = stringToPrecomputedChunk('"></span>');
250+
// display. It's never visible to users. We use the template tag because it can be used in every
251+
// type of parent. <script> tags also work in every other tag except <colgroup>.
252+
const placeholder1 = stringToPrecomputedChunk('<template id="');
253+
const placeholder2 = stringToPrecomputedChunk('"></template>');
253254
export function writePlaceholder(
254255
destination: Destination,
255256
responseState: ResponseState,
256257
id: number,
257258
): boolean {
258-
// TODO: This needs to be contextually aware and switch tag since not all parents allow for spans like
259-
// <select> or <tbody>. E.g. suspending a component that renders a table row.
260259
writeChunk(destination, placeholder1);
261260
writeChunk(destination, responseState.placeholderPrefix);
262261
const formattedID = stringToChunk(id.toString(16));

0 commit comments

Comments
 (0)