Preact: SSR rehydrate

Created on 11 Apr 2018  路  12Comments  路  Source: preactjs/preact

This is a question.

In React 16, when using SSR, in the client-side code, we need to use hydrate instead of render so that React only registers event handler. I guess that React does this by comparing the checksum between server-side rendered html and supposed-to-be client-side rendered html.

While playing with Preact and attempting to perform SSR, I notice that at first my component was rendered twice.
The rendering code is

// Server code
app.get("/", (req, res) => {
  const html = renderToString(<App />)
  res.render('index', { html })
})


// Client code
const root = document.getElementById("root")
render(<App />, root)

and the resulting html is

...
<div id="root">
  <div>This is my app.</div>
  <div>This is my app.</div>
</div>
...

When I change the client code to be

// Client code
const root = document.getElementById("root")
render(<App />, document.body, root)

The rendered html appears to be correct

...
<div id="root">
  <div>This is my app.</div>
</div>
...

but I wonder if it was the original html with preact event all hooked up, or if the content was replaced with exactly the same content.

Please help me shed some light on this.

Most helpful comment

There is no meaningful difference between hydrate() and render() within Preact, since we don't use checksums.

If you're seeing a flash of white or similar, check that you're not rendering something that is initially different from your server-rendered HTML. Also make sure you're interpreting the meaning of "parent" and "root" correctly:

const App = () => <div id="app">foo</div>

// our "server-side render":
document.write('<html><body><div id="root">' + renderToString(<App />) + '</div></body></html>')

// how "hydrate" from that:
render(<App />, document.getElementById('#root'), document.getElementById('#app'))

If you'd like a hydrate method, here's a simple one:

function hydrate(vnode, parent) {
  return preact.render(vnode, parent, parent.firstElementChild);
}

All 12 comments

Where are you injecting the script tag?

@nlhkh, I was running into the same issue. Found a solution in #24.

Hmm, I think I'm also seeing this. With the equivalent of

const root = document.getElementById("root")
render(<App />, root.parentElement, root)

it seems to blow away the contents of root rather than merging in <App /> (the two trees start off the same in my app; I'm seeing a flash of white where I would expect the DOM not to be touched).

React 16 has ReactDOM.hydrate for this purpose: https://reactjs.org/docs/react-dom.html#hydrate perhaps preact should support an equivalent?

There is no meaningful difference between hydrate() and render() within Preact, since we don't use checksums.

If you're seeing a flash of white or similar, check that you're not rendering something that is initially different from your server-rendered HTML. Also make sure you're interpreting the meaning of "parent" and "root" correctly:

const App = () => <div id="app">foo</div>

// our "server-side render":
document.write('<html><body><div id="root">' + renderToString(<App />) + '</div></body></html>')

// how "hydrate" from that:
render(<App />, document.getElementById('#root'), document.getElementById('#app'))

If you'd like a hydrate method, here's a simple one:

function hydrate(vnode, parent) {
  return preact.render(vnode, parent, parent.firstElementChild);
}

Sweet. This can be closed then, correct?

@developit Very helpful. Could that go in the docs somewhere?

There is no meaningful difference between hydrate() and render() within Preact, since we don't use checksums.

If you're seeing a flash of white or similar, check that you're not rendering something that is initially different from your server-rendered HTML. Also make sure you're interpreting the meaning of "parent" and "root" correctly:

const App = () => <div id="app">foo</div>

// our "server-side render":
document.write('<html><body><div id="root">' + renderToString(<App />) + '</div></body></html>')

// how "hydrate" from that:
render(<App />, document.getElementById('#root'), document.getElementById('#app'))

If you'd like a hydrate method, here's a simple one:

function hydrate(vnode, parent) {
  return preact.render(vnode, parent, parent.firstElementChild);
}

It works when the component has a root element but It doesn't work using Fragment.

For that case is better to use the normal render:

preact.render(vnode, parent);

For completeness: We do ship with a hydrate function in Preact X. It bypasses all diffing and only attaches event listeners. For it to work you must make sure that the SSR result matches the virtual tree on the client.

I have the same html tree as on the SSR but I also get flaky hydration results with some intermediate version where HTML is missing.

@bm-stschneider That sounds suspicious. Can you file a new issue for that?

I think that was a fault on my part, loading async data and loading that while initializing component elements, now I fetch all data before mounting that seems to work

Was this page helpful?
0 / 5 - 0 ratings

Related issues

KnisterPeter picture KnisterPeter  路  3Comments

mizchi picture mizchi  路  3Comments

simonjoom picture simonjoom  路  3Comments

kay-is picture kay-is  路  3Comments

jescalan picture jescalan  路  3Comments