Svelte: Reactive statements have incorrect values in functions where dependent values are updated

Created on 20 Mar 2020  路  6Comments  路  Source: sveltejs/svelte

Describe the bug
Reactive statements have incorrect values in functions where dependent values are updated.

Example:

let count = 0;
$: even = count % 2 === 0

function handleClick() {
  count += 1;
  console.log({ count, even }) // output => { count: 1, even: true }
  // `even` will have incorrect value here, unless `tick()` is being called before it.
}

To Reproduce
REPL

Expected behavior
Reactive statements should have correct (updated) values even if used in a function where their dependent values are updated.

Severity
Critical - I think the current behavior is confusing and might lead to bugs. In fact, I have found this as I was having some weird bugs in production. In addition, using tick() to get the expected behavior in this case is odd and leads to code bloating.

reactivity docs

Most helpful comment

For performance reasons, $: reactive blocks are batched up and run in the next microtask. This is the expected behavior. This is one of the things that we should talk about when we figure out how and where we want to have a section in the docs that goes into more details about reactivity.

If you want something that updates synchronously and depends on another value, you can use a derived store:

<script>
    import { writable, derived } from 'svelte/store';
    const foo = writable(0);
    const even = derived(foo, $foo => $foo % 2 === 0);
    console.log($foo, $even);
    $foo = 1;
    console.log($foo, $even);
</script>

All 6 comments

I too have been confused by behavior like this. Perhaps a clearly defined way to isolate atomic units with synchronous reactivity would help those of us still working through the idiosyncrasies of reactivity.

We have discussed this on Discord I believe. Personally I think this is the expected behavior. It follows from how Javascript and the browser works, I think. Having it the other way could lead to strange bugs.

What could be good is a clarifying statement in the documentation.

@kevmodrome Yes, we have discussed it but I still don't think that this should be the behavior.

For performance reasons, $: reactive blocks are batched up and run in the next microtask. This is the expected behavior. This is one of the things that we should talk about when we figure out how and where we want to have a section in the docs that goes into more details about reactivity.

If you want something that updates synchronously and depends on another value, you can use a derived store:

<script>
    import { writable, derived } from 'svelte/store';
    const foo = writable(0);
    const even = derived(foo, $foo => $foo % 2 === 0);
    console.log($foo, $even);
    $foo = 1;
    console.log($foo, $even);
</script>

@Conduitry Oh, wow, it works like a charm!

Btw, I didn't know that you can directly assign values to stores like this.

$foo = 1;

I thought that you should always use set() or update(). I couldn't find it in the docs, is this official syntax and is it going to work in all cases?

Easy to miss, but yes it's official syntax and documented at https://svelte.dev/docs#4_Prefix_stores_with_$_to_access_their_values

Was this page helpful?
0 / 5 - 0 ratings