svelte icon indicating copy to clipboard operation
svelte copied to clipboard

beforeUpdate called twice with bound reference

Open larbear opened this issue 4 years ago • 6 comments

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.

larbear avatar Feb 23 '21 06:02 larbear

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 the beforeUpdate and that fixes the issue but that could have many possible side-effects

deviprsd avatar Apr 30 '21 14:04 deviprsd

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.

RaiVaibhav avatar Oct 13 '21 13:10 RaiVaibhav

The issue still exists. Tested on Svelte 3.47.0.

mihaon avatar Apr 28 '22 19:04 mihaon

The issue still exists in Svelte 3.52.0.

claudiofelber avatar Oct 21 '22 07:10 claudiofelber

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>

larbear avatar Dec 27 '22 00:12 larbear

This issue still exists 4.0.0.

larbear avatar Jun 24 '23 04:06 larbear

This issue still exists 4.0.5.

yuinchien avatar Sep 30 '23 17:09 yuinchien