Question

Why does the following event handler work in a Svelte component?

I have the following component Form.svelte:

<script>
   export let onDelete;
</script>
<form>
  <button type="submit">Update</button>
  <button on:click={onDelete()}>Delete</button>
</form>

and this is the page which is using the component:

<script>
  import Form from './Form.svelte';

  function onDelete() {
    console.log('deleted');
  }
</script>
<Form {onDelete} />

According to the Svelte docs and tutorials, we should define the event handler as

<button on:click={() => onDelete()}>Delete</button>

or

<button on:click={onDelete}>Delete</button>

So why does the above code in the Form.svelte component works?

Here is a REPL.

 5  43  5
1 Jan 1970

Solution

 4

I would say this is a bug (which apparently is fixed in Svelte 5).

The compiler adds some extra logic when the function comes from a property rather than being defined locally.

For a local function you get this:

dispose = listen(button1, "click", onDelete());

Here the function is invoked directly, not just on click, hence why one should use an arrow function or only reference the function rather than call it.

For a function passed as property, the function is wrapped like this:

dispose = listen(button1, "click", function () {
    if (is_function(/*onDelete*/ ctx[0]()))
        /*onDelete*/ ctx[0]().apply(this, arguments);
});

Since a new function is passed as the handler, execution is deferred.

The function is then executed in the if condition and if nothing is returned, the logic ends there. If onDelete were then to return a function, that would be executed as well.

2024-07-21
brunnerh