最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - How does Svelte reactivity work inside functions? - Stack Overflow

programmeradmin3浏览0评论

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 ponent:

<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: /repl/73de7d705ab3442786710cd88ea3f625?version=3.12.1

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 ponent:

<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

Share Improve this question asked Sep 17, 2019 at 9:08 LillemanLilleman 8,1215 gold badges30 silver badges37 bronze badges 0
Add a ment  | 

5 Answers 5

Reset to default 3

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...

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.

Seems your problem is cause by the bind: option. Having this will cause the foo to be updated twice: once for the 'action' and once behind the scenes when the binding kicks in.

Just removing the bind: should work

<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() {
        setTimeout(() => foo = !foo, 1000);
    }
</script>

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

This make sense because you do not want to have two-way-binding for the checked state to foo. You only want the checkbox to reflect the value of foo.

This binding issue isn't with Svelte's reactivity, it's using preventDefault on the click event. The HTML checked attribute never changes, because the checked attribute doesn't fire any JS events to update the DOM.

In Svelte, you don't need to set foo since it's already bound to the HTML state.

...
function action() {
        console.log(foo)
    }
</script>

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

But if you don't want HTML to change before something else, declaring action as async works fine with on|click|preventDefault:

async function action() {
        let data = await new Promise((resolve) => setTimeout(resolve, 500))
        foo = !foo
        console.log("changed!")
        return data
    }

I would suggest not to cancel the event with the usage of preventDefault, because it would stop the default behaviour of the checkbox.

And therefore the reactivity is broken. If you wrap the reassignment of foo in a setTimeout you just tricking the event loop and the default is not prevented.

So if you like to stop the propagation of the event use preventDefault with the suggestion from Filip.

    <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
        async function action(e) {
                e.preventDefault();
                setTimeout(() => {foo =! foo});

        }
    </script>

    Foo: { foo }<br />
    Checkbox: <input type="checkbox" bind:checked={ foo } on:click={ action } />
发布评论

评论列表(0)

  1. 暂无评论