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 component was rendered twice event without passing arguments #4472

Closed
jefer94 opened this issue Jan 3, 2024 · 8 comments
Closed

SSR component was rendered twice event without passing arguments #4472

jefer94 opened this issue Jan 3, 2024 · 8 comments

Comments

@jefer94
Copy link

jefer94 commented Jan 3, 2024

Which package(s) are affected?

Lit Core (lit / lit-html / lit-element / reactive-element), SSR (@lit-labs/ssr)

Description

My web components is rendering twice even without arguments, I read that if the arguments changed before it was rendered this should happens. Only a button is reactive. The following example is weird because I'm building SSR to an unsupported language

Reproduction

The web version was processed with

bun build components/*.ts components/vl-link.ts --outdir=dist/components --minify --splitting --target=b
rowser --watch

This component is rendering twice

// import {LitElement, css, html} from 'https://cdn.jsdelivr.net/gh/lit/dist@3/core/lit-core.min.js';
import {LitElement, css, html} from 'lit';
import {customElement, property} from 'lit/decorators.js';


function ___default() {}

@customElement('control-ultimate-button')
export class ControlUltimateButton extends LitElement {
  static styles = css`
    .base {
      color: #fff;
      outline: 0;
      border: 0;
      border-radius: 3px;
      padding: 10px 20px;
    }
    .white {
      background-color: rgb(248, 245, 245);
      color: #000;
    }
    .blue {
      background-color: rgb(10, 18, 169);
    }
    .yellow {
      background-color: rgb(232, 209, 94);
    }
    .red {
      background-color: rgb(191, 20, 20);
    }
  `;

  @property()
  label: string = '';

  @property()
  light: string = 'white';

  @property()
  onThirdClick: string | (() => void) = '___default'; // Assuming a string or function

  render() {
    console.log(this)
    console.log(this.attributes)
    
    return html`
      <button @click="${this._change_light}" class="base ${this.light}">
        ${this.label}
      </button>`;
  }

  private _change_light() {
    if (this.light == 'white') this.light = 'blue';
    else if (this.light == 'blue') this.light = 'red';
    else {
      this.light = 'yellow';

      setTimeout(() => {
        if (typeof this.onThirdClick === 'function') {
          this.onThirdClick();
          console.log(111)

        } else {
          eval(this.onThirdClick)();
          console.log(222)
        }

        const parentNode = this.parentNode as Node | null;
        if (parentNode) {
          parentNode.removeChild(this);
        }
      }, 0);
    }
  }
}

Input

<!DOCTYPE html>
<head>
  <script type="module" src="/components/control-ultimate-button.js"></script>
  <script type="module" src="/components/vl-link.js"></script>

</head>
<body>
  <vl-link href="https://www.vlaanderen.be">Home</vl-link>
  <vl-link href="https://www.vlaanderen.be">Sign-up</vl-link>
  <vl-link href="https://www.vlaanderen.be">Sign-in</vl-link>
  <p class="headings">Hello, World!</p>



    <control-ultimate-button></control-ultimate-button>


  <script>
    function x() {
      console.log('done')
    }
  </script>
</body>`

The input to SSR was (?s)<body.*?>(.*?)<\/body>

This code with/without { deferHydration: true } does not work

import {render} from '@lit-labs/ssr';
import {
  collectResultSync,
} from '@lit-labs/ssr/lib/render-result.js';
import { unsafeHTML } from 'lit/directives/unsafe-html.js';

import {html} from 'lit';

import './components/control-ultimate-button.js'
import './components/vl-link.js'

// ...

  const template = html`${unsafeHTML(body.html)}`;
  const ssrResult = render(template);
  collectResultSync(ssrResult);

Output

<!DOCTYPE html>

<head>
	<script type="module" src="/components/control-ultimate-button.js"></script>
	<script type="module" src="/components/vl-link.js"></script>

</head>

<body>
	<!--lit-part BRUAAAUVAAA=-->
	<!--lit-part 7y4SaA82c+c=-->
	<vl-link href="https://www.vlaanderen.be" defer-hydration><template shadowroot="open" shadowrootmode="open">
			<!--lit-part 5TLwDNN+0jg=-->
			<link href="https://cdnjs.cloudflare.com/ajax/libs/flowbite/2.2.1/flowbite.min.css" rel="stylesheet" />
			<!--lit-node 1--><a href="https://www.vlaanderen.be"
				class="font-medium text-blue-600 dark:text-blue-500 hover:underline">
				<slot></slot>
			</a>
			<!--/lit-part--></template>Home</vl-link>
	<vl-link href="https://www.vlaanderen.be" defer-hydration><template shadowroot="open" shadowrootmode="open">
			<!--lit-part 5TLwDNN+0jg=-->
			<link href="https://cdnjs.cloudflare.com/ajax/libs/flowbite/2.2.1/flowbite.min.css" rel="stylesheet" />
			<!--lit-node 1--><a href="https://www.vlaanderen.be"
				class="font-medium text-blue-600 dark:text-blue-500 hover:underline">
				<slot></slot>
			</a>
			<!--/lit-part--></template>Sign-up</vl-link>
	<vl-link href="https://www.vlaanderen.be" defer-hydration><template shadowroot="open" shadowrootmode="open">
			<!--lit-part 5TLwDNN+0jg=-->
			<link href="https://cdnjs.cloudflare.com/ajax/libs/flowbite/2.2.1/flowbite.min.css" rel="stylesheet" />
			<!--lit-node 1--><a href="https://www.vlaanderen.be"
				class="font-medium text-blue-600 dark:text-blue-500 hover:underline">
				<slot></slot>
			</a>
			<!--/lit-part--></template>Sign-in</vl-link>
	<p class="headings">Hello, World!</p>



	<control-ultimate-button defer-hydration><template shadowroot="open" shadowrootmode="open">
			<style>
				.base {
					color: #fff;
					outline: 0;
					border: 0;
					border-radius: 3px;
					padding: 10px 20px;
				}

				.white {
					background-color: rgb(248, 245, 245);
					color: #000;
				}

				.blue {
					background-color: rgb(10, 18, 169);
				}

				.yellow {
					background-color: rgb(232, 209, 94);
				}

				.red {
					background-color: rgb(191, 20, 20);
				}
			</style>
			<!--lit-part QsMqw5KdZ7U=-->
			<!--lit-node 0--><button  class="base white">
        <!--lit-part--><!--/lit-part-->
      </button>
			<!--/lit-part-->
		</template></control-ultimate-button>


	<script>
		function x() {
      console.log('done')
    }
	</script>
	<!--/lit-part-->
	<?><!--/lit-part--></body>

The button was rendered twice
image

Workaround

I have not found a workaround

Is this a regression?

No or unsure. This never worked, or I haven't tried before.

Affected versions

lit@3.1.0 and @lit-labs/ssr-client@1.1.5

Browser/OS/Node environment

Opera One(version: 106.0.4998.19)
Linux archlinux 6.6.8-zen1-1-zen #1 ZEN SMP PREEMPT_DYNAMIC Thu, 21 Dec 2023 19:00:41 +0000 x86_64 GNU/Linux
bun 1.0.20

@jefer94
Copy link
Author

jefer94 commented Jan 3, 2024

My button appears two times inside my custom element, I think this must be updated after be rendered
image

@augustjk
Copy link
Member

augustjk commented Jan 3, 2024

I suspect you're missing the lit-element-hydrate-support module.
See https://lit.dev/docs/ssr/client-usage/#loading-@lit-labsssr-clientlit-element-hydrate-support.js

Make sure that script is added before your vl-link component definition at the top of <head> or bundled into the component definition before any lit imports.

This ensures that on first update, lit element won't create a new shadowroot with content if it detects an existing shadowroot due to the declarative shadow dom having been parsed.

@jefer94
Copy link
Author

jefer94 commented Jan 3, 2024

I added that right now and it stills not working

import {render} from '@lit-labs/ssr';
import {
  collectResultSync,
} from '@lit-labs/ssr/lib/render-result.js';
import { unsafeHTML } from 'lit/directives/unsafe-html.js';
import '@lit-labs/ssr-client/lit-element-hydrate-support.js';

import {html} from 'lit';
import { Elysia } from 'elysia'

const cache = {}

async function renderIndex({body}) {
  if (body && body.html === undefined) {
    return {message: 'body is empty', code: 'no-body'}
  }

  if (body && body.html === undefined) {
    return {message: 'html is required', code: 'no-html'}
  }

  if (body && body.dependencies === undefined) {
    return {message: 'dependencies is required', code: 'no-dependencies'}
  }

  const pending: Promise<any>[] = []

  for (let dep of body.dependencies) {
    if (cache[dep] === undefined) {
      const module = import(dep)
      cache[dep] = module
      pending.push(module)
    }
  }

  await Promise.all(pending)

  const template = html`${unsafeHTML(body.html)}`;
  const ssrResult = render(template, { deferHydration: true });

  return {html: collectResultSync(ssrResult)}
}

new Elysia()
  .post('/', renderIndex)
  .get('/test', () => ({status: 200, body: 'test'}))
  .listen(3000)

@augustjk
Copy link
Member

augustjk commented Jan 3, 2024

It needs to be added in the client code, not the server code.

@jefer94
Copy link
Author

jefer94 commented Jan 3, 2024

Thanks @augustjk this was fixed but I think these steps must be placed here https://www.npmjs.com/package/@lit-labs/ssr , I thought that fixture is included without include anything else

I thought I made an API rest decoupled that should be used as a lambda function or a microservice, are some of you interested in this? some teams still have issues with the modularity in their backend when they need to render HTML. I got fix how to do it yesterday

@augustjk
Copy link
Member

augustjk commented Jan 3, 2024

We do have it in the README, https://www.npmjs.com/package/@lit-labs/ssr#:~:text=To%20hydrate%20LitElement%20shadow%20roots%2C%20load%20the%20%40lit%2Dlabs/ssr%2Dclient/lit%2Delement%2Dhydrate%2Dsupport.js%20module, but it is hard to find so perhaps we can emphasize that more. Thanks for the callout!

I think there certainly is interest in doing SSR in serverless functions or edge workers (see #3907). I'm sure people can benefit from any findings you're able to share. We have a great community on discord (https://lit.dev/discord) or https://github.com/lit/lit/discussions is a good place for general discussion on github too.

@jefer94
Copy link
Author

jefer94 commented Jan 4, 2024

Well I published a new discussion to show what I have #4475

@augustjk
Copy link
Member

I'll close this issue as completed for adding the hydrate support module. Thanks for opening the discussion on SSR in non-JS environments!

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

No branches or pull requests

2 participants