Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Bug?]: Hydration Mismatch for passed JSX elements #1568

Closed
2 tasks done
illiaChaban opened this issue Jul 1, 2024 · 6 comments
Closed
2 tasks done

[Bug?]: Hydration Mismatch for passed JSX elements #1568

illiaChaban opened this issue Jul 1, 2024 · 6 comments
Labels
bug Something isn't working

Comments

@illiaChaban
Copy link

Duplicates

  • I have searched the existing issues

Latest version

  • I have tested the latest version

Current behavior 😯

When i pass jsx elements as a prop to my separation component I get hydration errors

export default function Home() {
  return (
    <main class="text-white">
      {/* works --> */}
      <Separate items={['hello 1']} />
      <Separate items={['hello 1', 'hello 2']} />
      {/* doesn't work --> */}
      <Separate items={[<span>Hello</span>, <span>world</span>]} />
    </main>
  );
}

const Separate = (p: { items: JSX.Element[] }) => {
  return (
    <p>
      <For each={p.items}>
        {(item, i) =>
          i() === p.items.length - 1 ? <>{item}</> : <>{item} • </>
        }
      </For>
    </p>
  );
};

Expected behavior 🤔

it should work without hydration errors "Hydration Mismatch. Unable to find DOM nodes for hydration key ..."

Steps to reproduce 🕹

Stackblitz link

Steps:

  1. Use this component:
export default function Home() {
  return (
    <main class="text-white">
      {/* works --> */}
      <Separate items={['hello 1']} />
      <Separate items={['hello 1', 'hello 2']} />
      {/* doesn't work --> */}
      <Separate items={[<span>Hello</span>, <span>world</span>]} />
    </main>
  );
}

const Separate = (p: { items: JSX.Element[] }) => {
  return (
    <p>
      <For each={p.items}>
        {(item, i) =>
          i() === p.items.length - 1 ? <>{item}</> : <>{item} • </>
        }
      </For>
    </p>
  );
};

Context 🔦

error "Hydration Mismatch. Unable to find DOM nodes for hydration key: 0-0-0-0-0-0-0-0-1-0-0-0-0-0-0-0-0-0-0-3-1-2"

Your environment 🌎

"dependencies": {
    "@solidjs/router": "^0.13.3",
    "@solidjs/start": "^1.0.2",
    "autoprefixer": "^10.4.19",
    "postcss": "^8.4.38",
    "solid-js": "^1.8.17",
    "tailwindcss": "^3.4.3",
    "vinxi": "^0.3.12"
  },
  "engines": {
    "node": ">=18"
  },
  "devDependencies": {
    "sass": "^1.77.6"
  }
@illiaChaban illiaChaban added the bug Something isn't working label Jul 1, 2024
@illiaChaban
Copy link
Author

Update: if i pass a function that returns JSXElement instead of JSXElement instead, it works
Although i don't think it's a fully expected behavior and I would still expect passing JSXElements to work
i.e

<Separate items={[() => <span>Hello</span>, () => <span>world</span>]} />

@mdynnl
Copy link

mdynnl commented Jul 3, 2024

This is expected with current state of hydration.

<Separate items={[<span>Hello</span>, <span>world</span>]} />

compiles to

_$createComponent(Separate, {
  get items() {
    return [_tmpl$(), _tmpl$2()];
  }
})

and accessing p.items generates new elements.
as far as the hydration is concerned, there is nothing wrong with that as long as these elements can be found in the DOM.

👉 But new elements created while evaluating p.items.length won't exist in the dom hence the hydration error.

i() === p.items.length - 1 ? <>{item}</> : <>{item}</>

this doesn't happen with

<Separate items={[() => <span>Hello</span>, () => <span>world</span>]} />

or

/* 🛑 use at your own risk */
const items = [<span>Hello</span>, <span>world</span>]
<Separate items={items} /> // no getter

or

function Separate(p) {
	/* ❌ don't do this */
	const items = p.items // just for the sake of the point

	const slightlyCorrect = createMemo(() => p.items)

	/* ✅ do this */
	const correct = children(() => p.items)
}

@illiaChaban
Copy link
Author

@mdynnl how is this expected? From the DX point of view it doesn't make much sense and this behavior is not documented anywhere.
Thanks for explaining on why it doesn't work due to some internal implementation details, however, IMO this is not an expected behavior, since i would expect SSR and SPA versions of Solid to work the same way

@illiaChaban
Copy link
Author

why can't i generate a new elements and not append them to the DOM? Why can't they be hydrated as part of the javascript?

@doeixd
Copy link
Contributor

doeixd commented Jul 8, 2024

Related Issue

@ryansolid
Copy link
Member

Yeah.. this is hard in general. The code above would be not good even in a purely client side app.. recreating elements over and over again. I'm going to close this as a duplicate of the linked issue in Solid core repo. But in general don't do this. Use the children helper if you are going to access a JSX rendering prop more than once.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

4 participants