WebReflection / heresy-ssr

🔥 heresy 🔥 Server Side Rendering
ISC License
88 stars 1 forks source link

Autonomous elements initialization #24

Closed CaptainJKoB closed 3 years ago

CaptainJKoB commented 3 years ago

Hey 👋

Found some different behaviour between autonomous and built-in elements. If you define a component that extends element it gets initialized multiple times. The final html output contains the last initialized component. This behaviour is not present in heresy (Codepen) Works the same for ES6 class declaration.

Env: Node: 14.4.0

Reproduction sample:

const http = require('http');
const {document, render, html, define} = require('heresy-ssr');

let firstElemInits = 0;
let secondElemInits = 0;

const FirstElement = {
  name: 'FirstElement',
  extends: 'element',
  oninit() {
    console.log('FirstElement init:', ++firstElemInits);
  },
  render() {
    this.html`FirstElement: ${ firstElemInits }`;
  }
}
const SecondElement = {
  name: 'SecondElement',
  extends: 'button',
  oninit() {
    console.log('SecondElement init:', ++secondElemInits);
  },
  render() {
    this.html`SecondElementID: ${ secondElemInits }`;
  }
}

define(FirstElement);
define(SecondElement);

render(document.body, html`
  <FirstElement />
  <SecondElement />
`);

const server = http.createServer(function(req, res) {
  res.writeHead(200, {"content-type": "text/html;charset=utf-8"});
  res.end(document.toString());
}).listen(8080, () => console.log("server running"))

Current console output:

FirstElement init: 1
FirstElement init: 2
SecondElement init: 1

Expected console output:

FirstElement init: 1
SecondElement init: 1
WebReflection commented 3 years ago

If I understand correctly the output is correct, but the "dance" behind the heresy-ssr scene is not, right?

CaptainJKoB commented 3 years ago

Yes, seems like it. Another thing i guess may be linked to this one is that if your render the elements in the example like this:

render(document.body, html`
  <FirstElement props="${{ name: 'Gary' }}" />
  <SecondElement props="${{ name: 'Gary' }}" />
`);

In oninit of the component extending element, the props will return undefined, in other cases it returns the set value.

WebReflection commented 3 years ago

OK, I've played around a bit with your example ... and here are my findings:

If you define a component that extends element it gets initialized multiple times.

That's not exactly correct. Try this instead:

const FirstElement = {
  name: 'FirstElement',
  extends: 'element',
  oninit() {
    this.firstElemInits = this.firstElemInits || 0;
    console.log('FirstElement init:', ++this.firstElemInits);
  },
  render() {
    this.html`FirstElement: ${ firstElemInits }`;
  }
};

You will see the element is initialized once, and once only.

The final html output contains the last initialized component.

That is correct.

This behaviour is not present in heresy

That is because herey there uses native DOM behavior, SSR needs to work around the fact there is no DOM, so this might be a basicHTML bug instead, but I'm not 100% sure yet.

Works the same for ES6 class declaration.

Yeah, I think what happens is that there is some synchronous initialization and replacement in basicHTML that triggers oninit right away.

The good news is that the layout is the expected one, so the result doesn't break expectations (except for coupling init with non-component data).

The bad news is that this might take a while to be fixed properly, if I can fix it properly.

Meanwhile, I've realized Node 15 breaks completely ... which is a bigger bummer.

WebReflection commented 3 years ago

P.S. ok, the NodeJS 15 error is gone ... one thing per time ...

CaptainJKoB commented 3 years ago

Interesting findings. Thank you for the insights.

The bad news is that this might take a while to be fixed properly, if I can fix it properly.

That's understandable. Opting towards built-in elements, anyways. I was just trying out different approaches.

Meanwhile, I've realized Node 15 breaks completely ... which is a bigger bummer.

Yeah, Node 15 is quite a bummer 😄

WebReflection commented 3 years ago

Fixed, it was a basicHTML bug indeed 👋