Skip to content

Commit

Permalink
Use extraHead to support nested components
Browse files Browse the repository at this point in the history
  • Loading branch information
patdx committed Jun 12, 2023
1 parent 545fc35 commit 1bb4a7d
Show file tree
Hide file tree
Showing 4 changed files with 48 additions and 7 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,21 @@ const SLEEP_MS = 10;

const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));

export function AsyncComponent() {
export function AsyncComponent(props) {
const [data] = createResource(async () => {
await sleep(SLEEP_MS);
// console.log("Start rendering async component " + props.title);
await sleep(props.delay ?? SLEEP_MS);
// console.log("Finish rendering async component " + props.title);
return 'async_result_from_async_component';
});

return (
<div data-name="AsyncComponent" onClick={() => actions.refetch()}>
{data()}
{/* NOTE: The props.children are intentionally commented out
to simulate a situation where hydration script might not
be injected in the right spot. */}
{/* {props.children} */}
</div>
);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
---
import { AsyncComponent } from '../components/async-components.jsx';
---

<html>
<head><title>Nested Test</title></head>
<body>
<div>
<AsyncComponent client:load title="level-a">
<AsyncComponent client:load title="level-a-a" ></AsyncComponent>
<AsyncComponent client:load title="level-a-b">
<AsyncComponent client:load title="level-a-b-a"></AsyncComponent>
</AsyncComponent>
<AsyncComponent client:load title="level-a-2" ></AsyncComponent>
</AsyncComponent>
</div>
</body>
</html>
9 changes: 9 additions & 0 deletions packages/astro/test/solid-component.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,15 @@ describe('Solid component', () => {
const hydrationScriptCount = countHydrationScripts(html);
expect(hydrationScriptCount).to.be.equal(0);
});

// nested.astro

it('Injects hydration script before any SolidJS components in the HTML, even if heavily nested', async () => {
const html = await fixture.readFile('nested/index.html');
const firstHydrationScriptAt = String(html).indexOf('_$HY=');
const firstHydrationEventAt = String(html).indexOf('_$HY.set');
expect(firstHydrationScriptAt).to.be.lessThan(firstHydrationEventAt);
});
});

if (isWindows) return;
Expand Down
18 changes: 13 additions & 5 deletions packages/integrations/solid/src/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -102,15 +102,23 @@ async function renderToStaticMarkup(

if (needsHydrate && renderStrategy === 'async') {
if (!ctx.hasHydrationScript) {
// Add the hydration script to the first hydrating component of the page
// The hydration script needs to come before to the first hydrating component of the page.
// One way to this would be to prepend the rendered output, eg:
//
// html += generateHydrationScript();
//
// However, in certain situations, nested components may be rendered depth-first, causing SolidJS
// to put the hydration script in the wrong spot.
//
// Therefore we render the hydration script to the extraHead so it can work anytime.

// NOTE: It seems that components on a page may be rendered in parallel.
// To avoid a race condition, this code block is intentially written
// To avoid a race condition, this code block is intentionally written
// *before* the first `await` in the function, so the hydration script will
// be prefixed to the first hydratable component on the page, regardless of
// the order in which the components finish rendering.
html += generateHydrationScript();
// I tried using extraHead but it did not work?
// this.result.extraHead.push(generateHydrationScript());

this.result.extraHead.push(generateHydrationScript());
ctx.hasHydrationScript = true;
}
}
Expand Down

0 comments on commit 1bb4a7d

Please sign in to comment.