Isolate context id space inside createComponent under NoHydration#2707
Isolate context id space inside createComponent under NoHydration#2707LeSingh1 wants to merge 1 commit into
Conversation
When the SSR shared context has noHydrate set, createComponent was calling the child component directly without pushing a new HydrationContext. That meant resources created in nested components shared the parent's id space, and because Suspense resets ctx.count to 0 inside its boundary, two resources at different positions could end up keyed by the same context id and the inner resource would read back the outer one's value. Always isolate per-component, even under noHydrate. nextHydrateContext already preserves the noHydrate flag via spread, so downstream hydration markers are still skipped; the only behavior change is that resource ids stay unique across components inside <NoHydration>. Closes solidjs#2546
|
|
Yeah as I commented in the original issue the challenge here is downstream libraries in the ecosystem like SolidStart literally using the equivalent of Dummy components to match id gen between client/server where the mount and ssr render points are different. I can't make this change without potentially breaking libraries that depend on this behavior. Which is very awkward at this point. At minimum we'd need to makie it a minor release and then be like for this version of Start you need atleast this version of Solid. But more than likely this is just something that gets fixed in Solid 2.0 (and it already is). |
Closes #2546.
Per the existing maintainer analysis in the issue thread (#2546 / #2131): under
<NoHydration>, IDs aren't managed correctly, so twocreateResourcecalls in nested components can collide on the same key incontext.resources.Walking through the repro (https://github.com/fongandrew/solidjs-resource-repro):
createComponentwas:Under noHydrate the function returned the child directly without pushing a new
HydrationContext. So:postresource getsid = "parent-0",countbecomes 1.<Suspense>reads the current id ("parent-1") and then runs its body withsetHydrateContext({ ...ctx, count: 0 })— count is reset for the suspense boundary.userresource getsid = "parent-0"again because count was reset by Suspense — collision withpost.resources["parent-0"].The fix is to always isolate the id space per component, even when noHydrate is set:
nextHydrateContextalready spreads...sharedConfig.context, so the new context inheritsnoHydrate: trueand downstream hydration markers continue to be skipped. The only behavior change is that the inner component's resource ids are derived from the outer component's freshly-allocated id, which keeps them unique across Suspense-driven count resets.Verified:
pnpm testin packages/solid → 469 / 469 pass (26 files).No regression test added because the existing test surface for renderToStringAsync + NoHydration lives in
solid-ssr/dom-expressionsrather than this package's vitest suite. Happy to add one if you'd like — would need a few lines of harness to wire updom-expressions/src/server.jshere.