skip to Main Content

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?

2

Answers


  1. 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 it is mentioned 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 or the above are false, there is no need to continue evaluating 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.

    Login or Signup to reply.
  2. The main issue is that not all parts of the logical expression are reactive, if they were, the $effect would retrigger reliably.

    As José Ramírez pointed out, the && expressions do not necessarily evaluate the second operator, but as long as the first one is reactive that should not be an issue. Once the first operator switches to being truthy, the $effect should execute and the second operator will become a dependency as well.

    So the root issue is that not all of these are reactive:

    • autoDetectSettings.value.mode
    • drawingBox
    • clicks.length

    If there is no way to make them reactive, I would probably evaluate the reactive dependency before the if as workaround as a workaround and annotate it with a comment. Otherwise it might break again later on due to refactorings.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search