Skip to content

Commit 00149b5

Browse files
authored
Merge pull request #8094 from QwikDev/v2-fix-use-id-ssr
fix: useId should genereate different id for ssr
2 parents 100cf87 + 2403f6a commit 00149b5

File tree

3 files changed

+47
-4
lines changed

3 files changed

+47
-4
lines changed

.changeset/shy-carpets-switch.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@qwik.dev/core': patch
3+
---
4+
5+
fix: useId should genereate different id for ssr

packages/qwik/src/core/tests/use-id.spec.tsx

Lines changed: 40 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
import { component$, componentQrl, inlinedQrl, useId } from '@qwik.dev/core';
1+
import { component$, componentQrl, inlinedQrl, useId, useSignal } from '@qwik.dev/core';
22
import { describe, expect, it } from 'vitest';
3-
import { domRender, ssrRenderToDom } from '@qwik.dev/core/testing';
3+
import { domRender, ssrRenderToDom, trigger } from '@qwik.dev/core/testing';
44

55
const debug = false; //true;
66
Error.stackTraceLimit = 100;
@@ -34,11 +34,47 @@ describe.each([
3434

3535
const { document } = await render(<Parent />, { debug });
3636
if (render === ssrRenderToDom) {
37-
expect(document.querySelector('#cmp1')?.textContent).toMatch(/^\w{3}cmp0$/);
38-
expect(document.querySelector('#cmp2')?.textContent).toMatch(/^\w{3}2cm1$/);
37+
expect(document.querySelector('#cmp1')?.textContent).toMatch(/^\w{3}cmp255s$/);
38+
expect(document.querySelector('#cmp2')?.textContent).toMatch(/^\w{3}2cm255t$/);
3939
} else {
4040
expect(document.querySelector('#cmp1')?.textContent).toMatch(/^cmp0$/);
4141
expect(document.querySelector('#cmp2')?.textContent).toMatch(/^Ccm1$/);
4242
}
4343
});
44+
45+
it('should generate different ids for csr and ssr', async () => {
46+
const Checkbox = component$((props: { label: string }) => {
47+
const id = useId();
48+
return (
49+
<div>
50+
<input type="checkbox" id={id} />
51+
<label for={id}>{props.label}</label>
52+
</div>
53+
);
54+
});
55+
56+
const Cmp = component$(() => {
57+
const enabled = useSignal(false);
58+
59+
return (
60+
<div>
61+
<h1>useId Example</h1>
62+
<Checkbox label="Item 1" />
63+
<button
64+
onClick$={() => {
65+
enabled.value = !enabled.value;
66+
}}
67+
></button>
68+
69+
{enabled.value && <Checkbox label="Subitem 1" />}
70+
</div>
71+
);
72+
});
73+
74+
const { document } = await render(<Cmp />, { debug });
75+
await trigger(document.body, 'button', 'click');
76+
const inputs = document.querySelectorAll('input');
77+
expect(inputs.length).toBe(2);
78+
expect(inputs[0].id).not.toBe(inputs[1].id);
79+
});
4480
});

packages/qwik/src/server/ssr-container.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -249,6 +249,8 @@ class SSRContainer extends _SharedContainer implements ISSRContainer {
249249
this.$buildBase$ = opts.buildBase;
250250
this.resolvedManifest = opts.resolvedManifest;
251251
this.renderOptions = opts.renderOptions;
252+
// start from 100_000 to avoid interfering with potential existing ids
253+
this.$currentUniqueId$ = 100_000;
252254

253255
const qlOpt = this.renderOptions.qwikLoader;
254256
this.qlInclude = qlOpt

0 commit comments

Comments
 (0)