qwik: [🐞] Tracking DOM Element reference causes QWIK WARN Serializing dirty task

Which component is affected?

Qwik Runtime

Describe the bug

  1. Get a signal of DOM element ref
  2. track() the signal.value in useTask$()
  3. A server warning occurs: QWIK WARN Serializing dirty task. Looks like an internal error.
  4. Doesn’t execute the useTask$ when the reference signal gets changed

Reproduction

https://stackblitz.com/edit/qwik-starter-f1bgnu?file=src%2Froutes%2Findex.tsx,package.json

Steps to reproduce

No response

System Info

System:
    OS: Linux 5.0 undefined
    CPU: (8) x64 Intel(R) Core(TM) i9-9880H CPU @ 2.30GHz
    Memory: 0 Bytes / 0 Bytes
    Shell: 1.0 - /bin/jsh
  Binaries:
    Node: 16.20.0 - /usr/local/bin/node
    Yarn: 1.22.19 - /usr/local/bin/yarn
    npm: 9.4.2 - /usr/local/bin/npm
    pnpm: 8.6.10 - /usr/local/bin/pnpm
  npmPackages:
    @builder.io/qwik: ^1.2.10 => 1.2.10 
    @builder.io/qwik-city: ^1.2.10 => 1.2.10 
    undici: 5.22.1 => 5.22.1 
    vite: 4.4.7 => 4.4.7

Additional Information

If you change the initial value of const shown to false (const shown = useSignal(false)) everything works ok.

About this issue

  • Original URL
  • State: closed
  • Created 10 months ago
  • Reactions: 2
  • Comments: 27 (11 by maintainers)

Most upvoted comments

can I take this issue and try to solve it @mhevery ?

Yes, but let’s discuss the solution first. I don’t think there is an easy fix, so I don’t want you to spend time on a solution that will not be mergeable into a repo.

Yep @damianpumar let’s go for it. thanks 💪

using useComputed$ also gives this error in a similar way, like in this following code:

    const swiper = useSignal<Element & { swiper: SwiperClass }>();
    const active = useComputed$(() => swiper.value ? swiper.value!.swiper.activeIndex : 0)

yes, it is the same issue. useCompute and useTask are essentially the same under the hood.

OK here is what is happening.

  • The useTask$ tracks ref.
  • The task runs before JSX and sets up a subscription on ref
  • The component is rendered, and the ref is updated with MockElement (since SSR). This update marks the task as dirty.
  • During serialization the QWIK WARN Serializing dirty task. Looks like an internal error. is shown because we are serializing a task that is dirty (Task needs to re-run) But during SSR we don’t re-run tasks.

Solution

It is not clear to me what the right solution is. Here are the options:

  • Suppress error, but that is kind of wrong since the task is dirty and needs to re-run. For example, during CSR you would expect the task to run twice. Once with ref of null and once when ref gets set. But in the case of SSR the second execution of the task is swallowed by the serializer. One can’t eagerly execute the tasks on the client as this is not a useVisibleTask$
  • Logically one would expect that the task runs twice. Once during initialization as part of SSR and then eagerly on the client once the ref is set to the actual DOM references. So is this another way to run code eagerly??? I don’t like that idea since in wast number of cases we are only interested when ref changes not when it is deserialized.

🤔

Workaround

  1. Ignore the error…
  2. Use useVisibleTask$ when you want to track if the element changes and want to be eagerly notified about the change.

Simpler reproduction:

import { component$, useSignal, useTask$ } from "@builder.io/qwik";

export default component$(() => {
  const ref = useSignal<HTMLElement>();

  useTask$(({ track }) => {
    track(() => ref.value);
  });
  return <h1 ref={ref}>TEST</h1>;
});