sveltejs/svelte

Async derived causes bind:this refs to revert to initial state when passed as props

Closed this issue ยท 17 comments

Describe the bug

The Popover from bits-ui is not opening under certain weird conditions.

Broken since 5.43.0

Edit: I have narrowed down the issue and made a REPL that has no bits-ui dependency to expose the svelte reactivity bug.

Reproduction

REPL

Logs

System Info

svelte playground

Severity

blocking an upgrade

7nik commented
  --bits-floating-transform-origin: undefined undefined;
  --bits-floating-available-width: undefinedpx;
  --bits-floating-available-height: undefinedpx;
  --bits-floating-anchor-width: undefinedpx;
  --bits-floating-anchor-height: undefinedpx;

I guess bits-ui miscalculates something or uses outdated references.

It works when you navigate back to the page though, but not after refreshing the page which makes me think it's a svelte bug but idk

7nik commented

The popover's logic is spread across dozens of files + floating-ui repo. It's too complex for us to unwrap the logic.
Open an issue in bits-ui. If it's Svelte's bug, the authors will come here with a simpler reproduction.

Duplicate of #17049

huntabyte/bits-ui#1882

I fixed it by adding this dependency to the $effect but my feeling is that there is a deeper svelte bug here related to the effect dependency chain. Sorry I don't know how to make a minimal repro :(

vyconm commented

[EDIT: potentially] fixed by #17105 in Svelte 5.43.4

fixed by #17105 in Svelte 5.43.34

No it's not. This is OP's REPL on 5.43.4. It's still broken.

7nik commented

@vyconm I don't see difference - Content still doesn't appear.

vyconm commented

Locally, in my private project, this has been fixed.

When I change the repl to: https://svelte.dev/playground/3029fe42f2fb4100bab1635964a4749f?version=5.43.4 , it seems to work again. Can someone confirm that svelte runs in async then?

In the original REPL, i don't really understand this line:

let foo = $derived(await open);

If you await open, it breaks. I'm not sure why open is being awaited either. If you await anything else, it works. However, awaiting open did work in 5.42.0 and breaks in 5.43.0.

@vyconm One thing to note though, is that your example (awaiting a promise) works in 5.43.0 - 5.43.3 as well.

vyconm commented

@sillvva could be that the playground is not detecting aync svelte?

another point might be the use of bind: to control the open state. There are other issues related to bind: and aync svelte here. My Private repo is always using the built-in trigger.

see this modified repl bugging out now that we use open to control state:

https://svelte.dev/playground/3029fe42f2fb4100bab1635964a4749f?version=5.43.4

vyconm commented

this might be the current "real" issue: #17090

async is enabled in the playground. And yeah, there are some issues with bind: in general at the moment.

I tried checking your latest REPL. EDIT deleted my own irrelevant comments. The popover component doesn't work without a trigger, whether you're using the trigger to toggle it or not. 5.43.0 REPL

7nik commented

await open resolves within the same macrotask, while await stall() (delaying via a timer) is resolved in another macrotask.
Likely some race happens.
But this REPL has a lot of logic under the hood, so it's tricky to find the problem.

This is still broken in the latest svelte version. Here's another interesting reproduction.

REPL

This time I don't bind the open state at all.

<script>
    import { Popover } from "bits-ui";

    let value = $state();

    let foo = $derived(await value);
</script>

<input bind:value />

<Popover.Root>
    <Popover.Trigger>Open Popover</Popover.Trigger>
    <Popover.Content>
		<p>Popover Content</p>
    </Popover.Content>
</Popover.Root>

{#if value}
    <p>lol</p>
{/if}

I made a minimal reproduction without using bits-ui!!

REPL