Hydration error for rendered Elements that aren't inserted in the DOM during server rendering
Describe the bug
SolidJS produces hydration errors when resolving children that aren't attached to the DOM.
Picture this:
const Component = (props) => {
const resolvedChildren = children(() => props.children)
return <Show when={false}>{resolvedChildren()}</Show>
}
Here, the children resolve but aren't attached to the actual DOM and it throws a hydration error.
Your Example Website or App
https://github.com/GiyoMoon/solid-hydration-error
Steps to Reproduce the Bug or Issue
- pnpm install
- pnpm dev
- go to localhost:3000
- see hydration error
Expected behavior
This shouldn't throw a hydation error.
Screenshots or Videos
No response
Platform
Solid 1.8.7 SolidStart 0.3.10
Additional context
No response
I'm generalizing the title because this issue impacts more than just children. This may be the most obvious but other locations are when people double render things for conditionals. Generally those are poor practices because they cause a lot of extra work but this issue can be a marker this.
Things like:
<Show when={<RenderComponent />}>
<RenderComponent />
</Show>
You should never ever ever do this. But I imagine there might be other patterns that we miss that are a bit more legit.
I'm generalizing the title because this issue impacts more than just
children. This may be the most obvious but other locations are when people double render things for conditionals. Generally those are poor practices because they cause a lot of extra work but this issue can be a marker this.Things like:
<Show when={<RenderComponent />}> <RenderComponent /> </Show>You should never ever ever do this. But I imagine there might be other patterns that we miss that are a bit more legit.
Hey @ryansolid, But then what would be the correct pattern? I would like to display a jsx element only if it exists though, conditionally, passed through a prop.
Another solution that I'm currently using, feels very clean to me: (This probably also applies to other use cases, not only children)
const keepAlive = createMemo((prev) => prev || show(), false)
return (
<Show when={keepAlive()}>
{(() => {
const resolvedChildren = children(() => props.children)
return <Show when={show()}>{resolvedChildren()}</Show>
})()}
</Show>
)
It does render on the server if show() is true initially, which is what we want!
Credits go to depict_daniel on Discord, such a great solution!
I want to clarify a bit because it might not be obvious to those reading this issue is we caused hydration errors to throw in in more cases post 1.8. Before 1.8 this would just kinda sneak by. We'd clone the nodes and not insert and it would probably work in a lot of cases, but we'd be creating DOM elements during hydration. I made a call with 1.8.1 to make things error more strictly because we no longer update DOM nodes during hydration either, so when people were getting things working by chance here other downstream changes were causing issues. The thinking was if we addressed this higher up with a straight up error it would be easier to track down. And it is.
However, I'm finding a lot of people were doing the double render pattern. Obviously it is not a good thing that they were but there is a question of how forgiving we should be of people doing these sort of footguns. I labelled this specific case as a bug becausethe user shouldn't ideally have to worry about this in a single render case. They are not abusing accessing the props multiple times. With proper use of the children helper it should resolve. The problem today is that even with children it can't find the DOM nodes so it will create and be missing attributes that aren't being written. The only solution for this is nesting the children call or lazily evaluating children in general so they won't be accessed until after hydration in cases when not inserted. Hence this is a 2.0 thing.
However even after 2.0 the double prop access is still a concern.
I found that it's also possible to use the solid-primitives's <Ref> component here as well for doing whatever is needed with the element