🐛 BUG: Astro + Solid. Hydration issue in nested islands.
What version of astro are you using?
1.0.0-beta.72
Are you using an SSR adapter? If so, which one?
doesn't apply
What package manager are you using?
pnpm
What operating system are you using?
Windows
Describe the Bug
I'm using Astro + Solid on my app and I think I might have found a hydration bug. It's a weird edge case involving nested islands but I'm using this pattern quite a lot. It's pretty much this:
// Solid components
function WrapperA() {
return (
<div>
<Counter type="A"></Counter>
</div>
);
}
function WrapperB(props) {
return (
<div>
<div>{props.children}</div>
<Counter type="B"></Counter>
</div>
);
}
function Counter(props) {
const [count, setCount] = createSignal(0);
return (
<button onClick={() => setCount((p) => ++p)}>
{count()}
</button>
);
}
// Astro component:
<WrapperB client:load>
<WrapperA client:load/>
</WrapperB>
In this setup, the WrapperB and his Counter inside aren't getting hydrated properly. The component is executed client-side, but with weird artifacts. For example, the ref prop accepts a callback that will be triggered when the component mounts to the dom — but here, the callback is getting called with the wrong element. The rest of the props, like event listeners aren't being applied correctly also.
Link to Minimal Reproducible Example
https://stackblitz.com/edit/astro-solid-bug?file=src%2Fpages%2Findex.astro&on=stackblitz
Participation
- [ ] I am willing to submit a pull request for this issue.
This is a tough one. I'm going to focus on the first case, but I think they are all probably the same underlying issue. The first case is:
<WrapperB client:load>
<WrapperA/>
</WrapperB>
It winds up looking like this:
The sharedContext.registry object that Solid uses internally has the wrong DOM nodes in this case. I think it's because they have the same data-hk values.
I suspect the way that we render and call ssr a couple of times and then renderToString, all separately, causes the keys to overlap. I think Solid probably expects only one call to happen.
Ok, so the underlying issue is that each renderToString creates a new non-shared context.
You can pass a renderId to renderToString which is used to generate the data-hk. I think if we give a distinct renderId for each call it will result the issue.
Ok, passing a renderId through does work. I need to write tests and a proper PR, but I think I have a fix.
Nice job! So Astro wasn't passing a unique id to the renderToString? I remember Ryan saying that this is something that he needs to do to support partial hydration properly. And it was a while ago. Guessing the adapter wasn't updated?
Yeah that's right, I have a PR that fixes it, #4215