qwik: [🐞] Qwik doesnt check `if-statements` before running child useComputed$ or useTask$

Which component is affected?

Qwik Runtime

Describe the bug

The following code causes Qwik to crash when the button is clicked. Conditionals aren’t checked before child component tasks are run when using Signals.

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

// A useTask$ in a child components circumvents the parent components conditional checks
const Child = component$((props: { val: string }) => {
  useTask$(({ track }) => {
    track(() => props.val)
  });
  return <>{props.val}</>;
});

export default component$(() => {
  const sig = useSignal<{ data: string } | undefined>({ data: '' });

  return (
    <>
      <button onClick$={() => (sig.value = sig.value ? undefined : { data: '' })}>Toggle</button>

      {/* ERROR: cannot read data of undefined */}
      {sig.value && <Child val={sig.value.data} />}
    </>
  );
});

Reproduction

https://github.com/DustinJSilk/qwik-issue-rendering/blob/main/src/routes/index.tsx

Steps to reproduce

Run the app Click the button

System Info

System:
    OS: macOS 12.0.1
    CPU: (12) x64 Intel(R) Core(TM) i7-9750H CPU @ 2.60GHz
    Memory: 116.35 MB / 16.00 GB
    Shell: 5.8 - /bin/zsh
  Binaries:
    Node: 18.12.1 - ~/.nvm/versions/node/v18.12.1/bin/node
    Yarn: 1.22.18 - /usr/local/bin/yarn
    npm: 7.13.0 - /usr/local/bin/npm
  Browsers:
    Chrome: 113.0.5672.126
    Firefox: 113.0.1
    Safari: 15.1
  npmPackages:
    @builder.io/qwik: ^1.1.4 => 1.1.4 
    @builder.io/qwik-city: ^1.1.4 => 1.1.4 
    undici: 5.22.1 => 5.22.1 
    vite: 4.3.5 => 4.3.5

Additional Information

No response

About this issue

  • Original URL
  • State: open
  • Created a year ago
  • Reactions: 2
  • Comments: 23 (16 by maintainers)

Commits related to this issue

Most upvoted comments

Sorry for the cryptic example!

What I was trying to show is that based on the contract of the Child component, as long as you don’t pass “bananas”, everything is fine. And naively, it does seem like we’re following that contract in that example.

Here’s another example to show the sorts of things a real world app might try to do.

I would describe the problem simply that type narrowing does not work on signals.

No matter what we write in the render logic, at runtime, the signal is not type narrowed, because it gets flushed through before the narrowing logic can run.

This can be a source of bugs that Typescript cannot detect.

Thanks @mhevery and @wmertens i appreciate you guys taking the time to think about this one!