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
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 ofclicks
won’t be read, and therefore the effect will not record it as a dependency) if:autoDetectSettings.value.mode
is not'box'
, ORdrawingBox
is falsy.Why? Because all 3 Boolean expressions are being joined with logical AND (
&&
). If either or the above arefalse
, 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.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.