How does Svelte reactivity work inside functions?

I have some serious problems with reactivity in Svelte. I have isolated what I think is at least one of my main issues. When bind:ing a variable to a checkbox, the reactivity seems to break when setting the variable inside a function, but not outside. Is this intended behavior? In that case why? And what is the intended work flow?

Example code, a Svelte component:

<script>
    let foo = true;

    // This assignment works both on the plain text view (Foo: true/false)
    // and on the checkbox
    // setInterval(() => foo = !foo, 500)

    // This assignment works only on the plain text view (Foo: true/false)
    // but not for the checkbox
    function action() {
        foo = !foo;
    }
</script>

Foo: { foo }<br />
Checkbox: <input type="checkbox" bind:checked={ foo } on:click|preventDefault={ action } />

Svelte REPL to see this problem in action: https://svelte.dev/repl/73de7d705ab3442786710cd88ea3f625?version=3.12.1


Solution 1:

If you wrap your variable declaration in a setTimeout without any time limit (making it default to 0 and therefore run as soon as possible) your code will work.

function action() {
    setTimeout(() => {
        foo = !foo;
    });
}

I wish I could explain why this works, but I can't...

Solution 2:

The best solution is much simpler, because the two-way binding by itself does everything you need. So, this does it:

<script>
  let foo = true;
</script>

Foo: { foo }<br />
Checkbox: <input type="checkbox" bind:checked={ foo } />

I know the question is two years old, but I think it needed better closure.