Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

on:click "Cannot read property 'apply' of undefined" #4090

Closed
ghost opened this issue Dec 10, 2019 · 6 comments
Closed

on:click "Cannot read property 'apply' of undefined" #4090

ghost opened this issue Dec 10, 2019 · 6 comments
Labels

Comments

@ghost
Copy link

ghost commented Dec 10, 2019

Describe the bug
Click handler throws an exception if handler function is stored as a variable and that variable is set somewhere in the <script> section.

We found this while attempting to fix #4087 via PR #4088.

To Reproduce
See repl:
https://svelte.dev/repl/7433e605325d4b4b87ea5522f5a5ff61?version=3.16.3

The exceptions started occurring from v3.13.0 onward.

Using same repl with older compiler produces expected results (no exceptions):
https://svelte.dev/repl/7433e605325d4b4b87ea5522f5a5ff61?version=3.12.1

Expected behavior
Click event shouldn't do anything (i.e. no exception).

Severity
Low; though it does violate the expectations for events on normal DOM elements.

Additional context

Offending generated code is:

dispose = [
	listen(button0, "click", /*updateHandler1*/ ctx[3]),
	listen(button1, "click", /*updateHandler2*/ ctx[4]),
	listen(button2, "click", function () {
		/*clickHandler*/ ctx[0].apply(this, arguments); // EXCEPTION HERE
	}),
	listen(button3, "click", /*runTest*/ ctx[5])
];
@ghost ghost changed the title on:click "Cannot read property 'apply' of undefined" on:<event> "Cannot read property 'apply' of undefined" Dec 10, 2019
@ghost ghost changed the title on:<event> "Cannot read property 'apply' of undefined" on:click "Cannot read property 'apply' of undefined" Dec 10, 2019
@Conduitry Conduitry added the bug label Dec 22, 2019
@Conduitry
Copy link
Member

Sort of adjacent to this and also to #4153, I was just thinking about the doc change made in #2944. It feels like in theory we should be able to undo that change, now that event handlers are reactive.

Indeed, this works:

<script>
	import ShoppingCart from './ShoppingCart.svelte';
	let cart;
</script>

<ShoppingCart bind:this={cart}/>

<button on:click={cart.empty}>
	Empty shopping cart
</button>

However, this does not:

<script>
	import ShoppingCart from './ShoppingCart.svelte';
	let cart;
</script>

<button on:click={cart && cart.empty}>
	Empty shopping cart
</button>

<ShoppingCart bind:this={cart}/>

(The cart && cart.empty is necessary because cart will still be undefined the first time we evaluate the event handler.) Clicking on the button does not call the cart.empty() method. I'm not sure why.

cc @tanhauhau

@tanhauhau
Copy link
Member

tanhauhau commented Dec 24, 2019

hmm.. interesting example, this is a totally different problem..........

bind:this does not mark the cart as reactive:

<ShoppingCart bind:this={cart} />

See repl that works.

Made a PR for the fix #4155

@Conduitry
Copy link
Member

What changed between 3.12.1 and 3.13.0 for the code that's generated here is:

dispose = listen(button, "click", ctx.handler);

became

dispose = listen(button, "click", function () {
	ctx.handler.apply(this, arguments);
});

Previously, we were just attaching undefined as an event listener, which the browser happily treated as a no-op when the event happened. Post-3.13, we are always explicitly trying to call it.

I'm not sure whether this change was intended to fix something else, or whether it was just the result of other structured code generation changes. If it were possible to change this back to ctx.handler (or, rather, ctx[4] or whatever) without breaking anything, I think that would be ideal, because it would be less code, and it would go back to letting the browser decide what was a no-op, like was happening in 3.12 and earlier.

@tanhauhau
Copy link
Member

we change from ctx.handler to function () { ctx.handler.apply(this, arguments); } only if ctx.handler is dynamic (reassigned/mutated).

why we wrapped it in function() {..} is discussed in #3836 (comment)

in summary is that instead of detach and reattach event listeners whenever ctx.handler is changed, we attach a listener function that will will always redirect to the latest ctx.handler, so we only need to addEventListener once, even if the handler is dynamic.

@ghost
Copy link
Author

ghost commented Dec 24, 2019

Things like this make me impatient for null-coelescing operators. I'll look deeper after the holiday.

@Conduitry
Copy link
Member

Fixed in 3.16.7.

rodshtein added a commit to rodshtein/svelte that referenced this issue Feb 9, 2022
Removed: > Note that we can't do `{cart.empty}` since `cart` is `undefined` when the button is first rendered and throws an error.

It fixed in 3.16.7
Issue: sveltejs#4090
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants