svelte
svelte copied to clipboard
beforeUpdate called twice with bound reference
Describe the bug This is a duplicate of #3290 which was closed, though the issue still exists.
beforeUpdate
is called twice when using bind:this
.
Logs No errors were shown in logs
To Reproduce https://svelte.dev/repl/3381c9b40dc8441a8e4ebfa48fd8c178 (credit to beomy)
<script>
import { beforeUpdate, afterUpdate } from 'svelte';
let p;
beforeUpdate(() => {
console.log('beforeUpdate');
})
afterUpdate(() => {
console.log('afterUpdate');
})
</script>
<p bind:this={p}></p>
Open the console to view the behavior. It produces:
beforeUpdate
afterUpdate
beforeUpdate
Expected behavior beforeUpdate should only be called once during a single update cycle.
beforeUpdate
afterUpdate
Severity
Severity is High, as the the cycle should always end with an afterUpdate
and not a repeated beforeUpdate
.
Other https://github.com/sveltejs/svelte/pull/3308, was made to potentially fix this issue but appears that it was never merged. The reason given was that it was fixed in 3.18.2, but the issue still exists. https://svelte.dev/repl/3381c9b40dc8441a8e4ebfa48fd8c178?version=3.18.2.
I might not have realized when I submitted them but there is one important concept change to my PR #3308.
It chooses to call the
bindings
before thebeforeUpdate
and that fixes the issue but that could have many possible side-effects
It happened because of the update call on the dirty component from schedulers i.e. when variable bind to the this, svelte mark it as a dirty and call the scheduler update
function update($$) {
if ($$.fragment !== null) {
$$.update();
run_all($$.before_update);
const dirty = $$.dirty;
$$.dirty = [-1];
$$.fragment && $$.fragment.p($$.ctx, dirty);
$$.after_update.forEach(add_render_callback);
}
}
In the above before_update gets called again and the same will happen if there are any reactive statements. Since I don't know the reason for calling the before update again, so I will be trying to dig deep into this issue and propose some initial solution.
The issue still exists. Tested on Svelte 3.47.0.
The issue still exists in Svelte 3.52.0.
Issue still exists in Svelte 3.55. Quick and hacky work-around by checking if the variable to store reference has been set:
https://svelte.dev/repl/28d44e6712604fe0b52c632e313f2359?version=3.55.0
<script>
import { beforeUpdate, afterUpdate } from 'svelte';
let p, count = 0;
let skipNextUpdate = false;
beforeUpdate(() => {
if (!skipNextUpdate) {
// Do beforeUpdate logic here.
console.log("beforeUpdate");
}
skipNextUpdate = !p;
})
afterUpdate(() => {
console.log("afterUpdate");
})
</script>
<p bind:this={p}>{count}</p>
<button on:click={() => count++}>Counter Up!</button>
This issue still exists 4.0.0.
This issue still exists 4.0.5.