I was reading the doc, and after tweaking its sample code, I managed to get compiler barked at me about cyclic dependencies like this:
<script>
let count = 0;
$: double = count * 2;
$: if (double >= 20) {
alert(`count is dangerously high!`);
count = 9;
}
function handleClick() {
count += 1;
}
</script>
<button on:click={handleClick}>
Clicked {count} {count === 1 ? 'time' : 'times'}
</button>
I asked on discord how to fix it, and people suggested that I should hide the dependencies from the compiler like this:
<script>
let count = 0;
$: double = count * 2;
function resetCount() {
count = 9;
}
$: if (double >= 20) {
alert(`count is dangerously high!`);
resetCount();
}
function handleClick() {
count += 1;
}
</script>
<button on:click={handleClick}>
Clicked {count} {count === 1 ? 'time' : 'times'}
</button>
It works, but I got a couple questions:
Thanks.
The answer from @morphyish sort-of provides a fix, because as they stated:
a reactive statement can't trigger itself
However, I see that as somewhat of a technicality, and still see the provided solution as conceptually having a cyclic dependency: we still have count -> double -> count -> ...
.
And because we've bypassed this cyclic dependency warning by merging the statements into a single reactive block, we've actually also introduced a bug:
This bug occurs because the double
value is set to 10 * 2
= 20 at the beginning of the reactive block, then count
is set to 9
within the if-statement, but then double
is not set back to 9 * 2 = 18
because the reactive block doesn't trigger again.
My suggestion in this, and similar cases would be to re-evaluate what your dependencies actually are in order to remove these cycles:
double = count * 2;
^ So double
depends on count
, that one's easy.
if (double >= 20) {
alert('count is dangerously high!');
count = 9;
}
^ At first glance it might seem like our count-reset logic depends on double
, but seeing as we've already established that double
depends on count
, and this block is ultimately concerned with count
, this logic really depends on count
, not double
.
So in my view, the best solution would be to modify the conditional to match the actual dependency:
<script>
let count = 0;
$: double = count * 2;
$: if (count >= 10) {
alert(`count is dangerously high!`);
count = 9;
}
function handleClick() {
count += 1;
}
</script>
<button on:click={handleClick}>
Clicked {count} {count === 1 ? 'time' : 'times'}
</button>
You could fix this issue by organizing your code slightly differently:
<script>
let count = 0;
let double;
$: {
double = count * 2;
if (double >= 20) {
alert(`count is dangerously high!`);
count = 9;
}
}
function handleClick() {
count += 1;
}
</script>
<button on:click={handleClick}>
Clicked {count} {count === 1 ? 'time' : 'times'}
</button>
You can group reactive statements together using {}
with a small caveat: Svelte won't automatically write the variable declaration as it would otherwise.
I've never run into this issue before, but in your case it looks like both statement are dependent on count
being updated, albeit indirectly for the second one. So it makes sense to actually group them into a single statement.
It also solves your issue as a reactive statement can't trigger itself.
However it means that if you want to also update double
you would need to do it explicitly.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With