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

SSR rehydrate #1060

Closed
khanetor opened this issue Apr 10, 2018 · 12 comments
Closed

SSR rehydrate #1060

khanetor opened this issue Apr 10, 2018 · 12 comments

Comments

@khanetor
Copy link

khanetor commented Apr 10, 2018

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.

@developit
Copy link
Member

Where are you injecting the script tag?

@Cinamonas
Copy link

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

@rattrayalex-stripe
Copy link

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).

@rattrayalex-stripe
Copy link

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

@developit
Copy link
Member

developit commented May 17, 2018

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);
}

@rattrayalex-stripe
Copy link

Sweet. This can be closed then, correct?

@mfbx9da4
Copy link

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

@marconi1992
Copy link
Contributor

marconi1992 commented Nov 15, 2019

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);

@marvinhagemeister
Copy link
Member

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.

@st-schneider
Copy link

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.

@marvinhagemeister
Copy link
Member

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

@st-schneider
Copy link

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

8 participants