The aim of hydratation is to avoid trashing out of existing DOM rendered by SSR, but use them
and attach eventual event listeners (like a regular patch
pass).
Hydratation would happen at the app
level and at the first render. Basically it needs a way to
know that some part of the DOM tree is something to patch
so it does not have to mount
.
Before going on, some terminology:
- The
root
is the root DOM node generated by HyperApp, the toplevel DOM element. - The
host
is the DOM node that will hold theroot
, it's currently defined asapp.root
.
The current implementation simply appends the root
to the host
. So if the host
already
contains children, they are not removed. That is an important feature because it allows non-HyperApp elements to live aside the root
, sharing the same host
.
Let's say we have a very basic app that produce the following DOM elements:
<div class="app">
<h1>Counters</h1>
<button class="counter">1</button>
</div>
The root of the app is div.app
. Now lets say we have an already existing document containing terrible stuff like social crap:
<body>
<div id="fb-root"></div>
<script src="my.very.useless.facebook-script.js" />
</body>
Currently we can implicitly inject the app using body
as the host
, the result will be:
<body>
<div id="fb-root"></div>
<script src="my.very.useless.facebook-script.js" />
<div class="app">
<h1>Counters</h1>
<button class="counter">1</button>
</div>
</body>
Now imagine we use SSR, the initial document becomes:
<body>
<div id="fb-root"></div>
<script src="my.very.useless.facebook-script.js" />
<div class="app">
<h1>Counters</h1>
<button class="counter">1</button>
</div>
</body>
And when HyperApp kicks in, it becomes:
<body>
<div id="fb-root"></div>
<script src="my.very.useless.facebook-script.js" />
<div class="app">
<h1>Counters</h1>
<button class="counter">1</button>
</div>
<div class="app">
<h1>Counters</h1>
<button class="counter">1</button>
</div>
</body>
So it basically duplicates things because it has no information to distinct initial markup from SSRed markup.
The solution I propose is to put a marker attribute on the root
rendered DOM, let's ssr
. So the initial markup now becomes this:
<body>
<div id="fb-root"></div>
<script src="my.very.useless.facebook-script.js" />
<div class="app" ssr>
<h1>Counters</h1>
<button class="counter">1</button>
</div>
</body>
When HyperApp kicks in, it will simply seek for the first child of the host
(aka app.root
) having the attribute ssr
. If it finds it, it will considered as the root
(aka element
in HyperApp source code) and will be patched.
If no ssr
child is found, the root
(aka element
in Hyper source code) is left to undefined
and patch will mount the app (aka create all DOM nodes).