javascriptsvelte

Reactivity in $effect goes away if I remove a console.log()


I have a $state with an array:

let clicks = $state([])

I have an effect, which references the clicks state:

$effect(() => {
    // Box selection
    if (autoDetectSettings.value.mode === 'box' && drawingBox && clicks.length === 2) {
        const poly = objectSchema.parse({
            id: createId(),
            type: 'poly',
            coords: [
                { x: clicks[0].x, y: clicks[0].y },
                { x: clicks[0].x, y: clicks[1].y },
                { x: clicks[1].x, y: clicks[1].y },
                { x: clicks[1].x, y: clicks[0].y },
            ],
            color: '#ffffff',
            opacity: 0.15,
            stroke_color: '#ffffff',
            stroke_opacity: 1,
            stroke_width: 2,
            blend_mode: 'normal',
        })
        artboard.objects.push(poly)
    }
})

Adding an $inspect shows that the value of clicks is updating, but the effect is not triggering. Adding a console.log(clicks) at the start of the effect fixes the issue. What am I doing wrong?


Solution

  • Boolean Expression Short Circuiting

    Short Circuiting refers to JavaScript's ability to forego the evaluation of other Boolean expressions in a larger expression if the value can be inferred from evaluating the first sub-expressions of said larger expression.

    Svelte v5 And Short Circuiting

    Svelte v5 tracks effect dependencies in runtime. If a reactive value is expected to be read, but it ends up not being read because the expression using it has been discarded due to short-circuiting, then the effect won't record the dependency.


    In your case, the expression clicks.length === 2 will not be evaluated (and therefore, value of clicks won't be read, and therefore the effect will not record it as a dependency) if:

    1. autoDetectSettings.value.mode is not 'box', OR
    2. drawingBox is falsy.

    Why? Because all 3 Boolean expressions are being joined with logical AND (&&). If either of the above is false, there is no need to continue evaluating the other pieces.

    Solution

    Move clicks.length === 2 to the beginning of the IF statement.


    I am blogging about Svelte v5 Reactivity in this series. The next article is half-baked and is about $effect and things exactly like this one.