atellmer / dark

The lightweight and powerful UI rendering engine without dependencies and written in TypeScript💫 (Browser, Node.js, Android, iOS, Windows, Linux, macOS)
MIT License
41 stars 1 forks source link

`paltform-browser`'s `render` function could handle entire document #44

Closed einar-hjortdal closed 5 months ago

einar-hjortdal commented 6 months ago

paltform-browser's render function rejects document as parameter because document instanceof Element evaluates to false. A few small changes should allow the render function to correctly handle the entire document as root.

atellmer commented 6 months ago

I just wrote in the next issue why document is not supported. Because besides our application, there are other sources that can influence the DOM and we do not control these sources.

atellmer commented 6 months ago

I mean something like that. 1 These are unpredictable DOM elements that are introduced by browser extensions. And we will inevitably get mismatch errors if we compare the actual DOM tree and the Fiber tree.

einar-hjortdal commented 6 months ago

I just wrote in the next issue why document is not supported. Because besides our application, there are other sources that can influence the DOM and we do not control these sources.

This is a very good point. I have quickly investigated how React handles this issue because I never encountered it myself while using React. It turns out React doesn't seem handle it either on 18.2 release, as described in this open issue. But it seems a solution was pushed to 18.3 (still investigating)

I think a possible solution to this problem, besides the workaround shown in the dark docs, would probably be to mark server-rendered elements with adjacent comments (inspired by qwik), or with element attributes, and make the hydration process ignore non-marked elements.

atellmer commented 6 months ago

It seems to me that a good solution is one that does not require writing any code and still works flawlessly. I came to this solution: splitting the code into Page (root HTML) and App. In this case, the App is inserted into a Page of type

<Page>
  <App />
</Page>

As a result, we get full SSR generation, but at the same time we hydrate only the App. An example is here

einar-hjortdal commented 6 months ago

It seems to me that a good solution is one that does not require writing any code and still works flawlessly. I came to this solution: splitting the code into Page (root HTML) and App. In this case, the App is inserted into a Page of type

<Page>
  <App />
</Page>

As a result, we get full SSR generation, but at the same time we hydrate only the App. An example is here

Yes indeed this technique works perfectly. But if hydrateRoot can accept the whole document, without adding comments/attributes and adding more complexity to the hydration process, it would be nice.

atellmer commented 6 months ago

For me, this problem can only lie in the plane of beauty, but not functionality. Yes, it would be nice to be able to work with document, although from a practical point of view this would entail the problems described above. If we can fix this by refactoring some of the code, then that will be good. However, if this implementation requires writing complex logic, then this intervention does not seem necessary, because we will simply increase the complexity of the system, while solving a problem for which there is already a solution.

atellmer commented 6 months ago

I added this

import { h, component, useState } from '@dark-engine/core';
import { hydrateRoot } from '@dark-engine/platform-browser';

const App = component(() => {
  const [count, setCount] = useState(0);

  return (
    <html lang='en'>
      <head>
        <meta charset='UTF-8' />
        <meta name='viewport' content='width=device-width, initial-scale=1.0' />
        <meta http-equiv='X-UA-Compatible' content='ie=edge' />
        <title>Test: {count}</title>
      </head>
      <body>
        <div id='root'>
          <div>{count}</div>
          <button onClick={() => setCount(x => x + 1)}>click</button>
        </div>
        <script src='./build.js' defer></script>
      </body>
    </html>
  );
});

hydrateRoot(document, <App />);

Looks very strange 🙃