preactjs / preact-render-to-string

📄 Universal rendering for Preact: render JSX and Preact components to HTML.
http://npm.im/preact-render-to-string
MIT License
653 stars 92 forks source link

Issue rendering components with hooks #402

Closed Scott-Fischer closed 1 month ago

Scott-Fischer commented 1 month ago

While utilizing useEffect hooks in my component, renderToString throws an error during function execution.

I was under the impression that components with hooks can be rendered on the server (by ignoring hooks) and prepared for client-side hydration (where the hooks are actually executed) but I can't seem to get that far because node throws while trying to do the initial markup rendering.

TypeError: Cannot read properties of null (reading '__H')

Here's a minimal reproducible example: https://stackblitz.com/edit/node-vztabj?file=index.js

marvinhagemeister commented 1 month ago

Calling your component functions yourself is not the same thing as letting Preact render and instantiate them. To render components with Preact you need to let Preact render them.

// WRONG, leads to the error you're seeing
renderToString(Component({ someProp: true });

// CORRECT
renderToString(<Component someProp />)
// also correct, basically doing jsx manually
renderToSTring(h(Component, { someProp: true }))

Applying that to your code:

  const ComponentWithOutHooks = renderToString(
-   WithoutHooks({ label: 'Click Me' })
+   <WithoutHooks label="Click Me" />
  );

- const ComponentWithHooks = renderToString(WithHooks({ label: 'Click Me' }));
+ const ComponentWithHooks = renderToString(<WithHooks label="Click Me" />);

Hooks can only work when Preact is rendering the outer component. At the time when you call the component function eagerly yourself, no rendering has started. The return value is instead passed to renderToString and only at this point rendering will begin. But no component context has been set up inside Preact that hooks expect to be there, hence the error. By passing the actual JSX node to Preact, the component function will be called at the expected time during Preact's rendering process.

Scott-Fischer commented 4 weeks ago

Thanks for the explanation @marvinhagemeister! I was almost positive this was user error on my part and your response makes it really clear where that was happening.