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

[✨] Add "eager" option to useClientEffect$ eagerness #2926

Closed
felixsanz opened this issue Feb 10, 2023 · 16 comments
Closed

[✨] Add "eager" option to useClientEffect$ eagerness #2926

felixsanz opened this issue Feb 10, 2023 · 16 comments
Labels
STATUS-1: needs triage New issue which needs to be triaged TYPE: enhancement New feature or request

Comments

@felixsanz
Copy link
Contributor

felixsanz commented Feb 10, 2023

Is your feature request related to a problem?

Currently useClientEffect$() let you specify how is executed with {eagerness:'visible|load|idle'}, but all of them are delayed.

Can we have something like eagerness: eager that puts the script inside <head>? (before <body>)

Describe the solution you'd like

^

Describe alternatives you've considered

Using a simple <script>, but prefer the qwik's way.

Additional context

No response

@felixsanz felixsanz added TYPE: enhancement New feature or request STATUS-1: needs triage New issue which needs to be triaged labels Feb 10, 2023
@mhevery
Copy link
Contributor

mhevery commented Feb 10, 2023

I thought load was as eagre as it gets. Why do you think it is not eager enough? What do you have in mind?

@felixsanz
Copy link
Contributor Author

felixsanz commented Feb 10, 2023

I thought load was as eagre as it gets. Why do you think it is not eager enough? What do you have in mind?

Using it for theming. load waits until all document is rendered and then executes, which may trigger flashes of styles and other issues. (like showing website in light mode and then switching to dark mode when reading localStorage / user preferences).

With this solution, this piece of JS will block rendering (intended), to retrieve the theme and then render correctly once

@mhevery
Copy link
Contributor

mhevery commented Feb 10, 2023

OK, that make sense. I think this is out of scope for v1

@manucorporat
Copy link
Contributor

it would be cool but technically very challenging:

  • Blocking means put it in the <head> this is hard to do with streaming, we might put in the network the head when we are rendering a new component, we can't go back and inject stuff in html that has already be sent!
  • QRL are always lazy loaded, so not sure how we can block it.

I cant think of any way, we can implement this

@felixsanz
Copy link
Contributor Author

felixsanz commented Feb 11, 2023

it would be cool but technically very challenging:

* Blocking means put it in the `<head>` this is hard to do with streaming

If i'm not wrong, you can put scripts wherever you want, even right after opening body tag, and rendering will block there. That's the reason we usually put scripts after all the HTML has been sent right?

I mean qwik can send the script tag at any moment and rendering will stop to execute the script, and then continue loading HTML, and after all HTML is loaded it will fire all eagerness: load/etc scripts.

I'm mistaken?

Just as qwik sends its core script (qwikloader), it can send all other eagerness: eager scripts as soon as possible?

@genki
Copy link
Contributor

genki commented Feb 12, 2023

Even if the {eagerness:'eager'} mentioned above is challenging, isn't it possible to be executed immediately when the component is mounted as like as <script> tag does?
So to speek {eagerness:'immediate'}.
I think it can be used for the flickerless theming if the theme name is in the cookie.

@genki
Copy link
Contributor

genki commented Feb 12, 2023

Having looked into the qwikloader, as it is placed at the very end of the HTML so it couldn't be used for resolving QRLs at the timing that components are mounted.
I think it can be accomplished if there is the global queue (ex window.qwikloads) for requesting to resolve QRL, and the qwikloader responds to do it before the begining of rendering.

@felixsanz
Copy link
Contributor Author

Even if the {eagerness:'eager'} mentioned above is challenging, isn't it possible to be executed immediately when the component is mounted as like as <script> tag does? So to speek {eagerness:'immediate'}. I think it can be used for the flickerless theming if the theme name is in the cookie.

But that won't work for static site (SSG), plus if its executed when component is mounted there is no difference to eagerness: load. It needs to be executed while html is parsing, in blocking/sync mode

@genki
Copy link
Contributor

genki commented Feb 12, 2023

@felixsanz
Even SSG, the codes enqueuing callbacks to the qwikloader can be generated as <script> statically, so there's a chance to changing styles and DOM tree depending on the client side info, such as the theme name in the cookie.
The timing that components are mounting is before the completion of the DOM tree building (the DOMContentLoaded event that causes the eagerness: load), it means before the rendering started.

@felixsanz
Copy link
Contributor Author

@felixsanz Even SSG, the codes enqueuing callbacks to the qwikloader can be generated as <script> statically, so there's a chance to changing styles and DOM tree depending on the client side info, such as the theme name in the cookie. The timing that components are mounting is before the completion of the DOM tree building (the DOMContentLoaded event that causes the eagerness: load), it means before the rendering started.

no, what I mean is that SSG doesn't use cookies as there is no server

@genki
Copy link
Contributor

genki commented Feb 13, 2023

@felixsanz
Even through not with cookies, you can use IndexedDB, caches API and so on without servers to accomplish the flickerless theming if before the DOMContentLoaded.
The script tag in <head> is not matter for it. It is really depending on before or after the document's readystate being interractive.

@genki
Copy link
Contributor

genki commented Feb 13, 2023

By the way, it is difficult to do something from the qwik component before the rendering because of the module script is always run after the document's ready.
To achieve this, the inline <script dangerouslySetInnerHTML={...} /> tag is needed in the resulting JSX.
All you want to do is capable in the script, except to access states of the container.
So the problem is how to pass the state to the script's scope.
I think similar work has already done by the qwik. It's a kind of resuming of client to client.
The solution can be like this:

  1. prepend the serialized state into the JSX.
  2. append `<script>' tag including handler script to the JSX
  3. the script tag starts with the state loader as like as the useLexicalScope in order to use the state from the handler.

@genki
Copy link
Contributor

genki commented Feb 15, 2023

I made a runnable PoC of doing it as a component <ClientEffect /> here :)
https://stackblitz.com/edit/qwik-starter-omdy7b?file=src/routes/index.tsx

@felixsanz
Copy link
Contributor Author

I made a runnable PoC of doing it as a component <ClientEffect /> here :) https://stackblitz.com/edit/qwik-starter-omdy7b?file=src/routes/index.tsx

Mmm, it seems to work fine, but I don't understand what's going on. Can you explain a bit? Also, is this expected to be used by the user or integrated in qwik somehow?

@genki
Copy link
Contributor

genki commented Feb 15, 2023

@felixsanz I just implemented the idea explained above.
The <ClientEffect /> has props
state: serialized and prepended into the <script> tag.
eager: callback called before the rendering. You can't access to things in the component except the state passed.
load$: QRL called when the {eagerness: load}. You can access to things in the component. To achieve this, a CustomEvent is used.
This is already usable, but there are rooms to be sophisticated as follows:

  • Though introduced as a workaround, maybe the setTimeout can be removed.
  • Using dispatch() of the qwikloader is better than triggering the CustomEvent.
  • Using serializer better than JSON.stringify()

@wmertens
Copy link
Member

For the theming you actually need to embed a script as high up as you can. I don't think Qwik can ever do what you want with a hook. ClientEffect can do the embedding but it looks overkill for this use case.

BTW, another option to prevent theme flashes is to apply all the CSS hiding classes from the start and then when JS is disabled add a style that overrides the hides, like <noscript><style>.hide-block { display: block !important; }</style></noscript>.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
STATUS-1: needs triage New issue which needs to be triaged TYPE: enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

5 participants