client component doesnt match rendered component explanation #94
Description
Users get this message:
Warning: React attempted to reuse markup in a container but the checksum was invalid. This generally means that you are using server rendering and the markup generated on the server was not what the client was expecting. React injected new markup to compensate which works but you have lost many of the benefits of server rendering. Instead, figure out why the markup being generated is different on the client or server:
and wonder what it is
The message comes right out of react, so you would think you could find a nice SO explanation but I didn't.
So here goes.
First a little background on server-side rendering (or prerendering.) Server-side rendering works by running your hyperloop components in a javascript engine on the server, and saving the resulting HTML output. This is then shipped down to the client as the initial page view. So server-side rendering makes your hyperloop code work just like any other view templating engine.
Once the page is loaded on the client, react then renders your components again. If the checksum of the stuff it just rendered matches the checksum of the server rendered code then it can simply attach event handlers without updating the DOM.
If the checksums don't match then it has to update the DOM, and it gives you a warning to let you know that something might be wrong.
How does this happen? Consider the following component:
class TheTime < Hyperloop::Component
render(DIV) do
"The time is #{Time.now}"
end
end
In this case the time will very likely be different between the server and client and so you will get the above warning.
These situations can almost always be fixed by moving some initialization to after_mount
. This is because after_mount
only runs on the client, never during pre-rerendering.
class TheTime < Hyperloop::Component
after_mount do
mutate.time Time.now
end
render(DIV) do
"The time is #{state.time}"
end
end
This will fix the problem because during both pre-rerendering and client rendering state.time
will be nil and the generated html will be <SPAN>The time is </SPAN>
. Then after client rendering is complete after_mount
will run, the time state will be mutated causing a re-rerender.
It might appear that we have not accomplished anything because end up rendering TheTime
component 3 times, and updating the DOM. However if TheTime
is just one component buried in a larger app then we have accomplished a lot, because only TheTime
has to be re-rerendered and not the whole component tree.