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

Custom-element=true issue with dispatch(); #2896

Closed
georges-gomes opened this issue May 28, 2019 · 19 comments
Closed

Custom-element=true issue with dispatch(); #2896

georges-gomes opened this issue May 28, 2019 · 19 comments
Labels
awaiting submitter needs a reproduction, or clarification

Comments

@georges-gomes
Copy link

I think it's an issue specific to custom-element.

the following component (from tutorial)

<script>
	import { createEventDispatcher } from 'svelte';

	const dispatch = createEventDispatcher();

	function sayHello() {
		dispatch('message', {
			text: 'Hello!'
		});
	}

</script>

<button on:click={sayHello}>
	Click to say hello
</button>

When I click the button I get a "TypeError: e is undefined"
It seems to come from "CreateEventDispatcher()"

createEventDispatcher(){const e=current_component;return(t,n)=>{const o=e.$...

current_component is not defined for custom-elements?

@Conduitry
Copy link
Member

I can't reproduce this. current_component is defined for me in a custom element. Can you push a complete repro to a repo somewhere?

@georges-gomes
Copy link
Author

georges-gomes commented May 29, 2019 via email

@Conduitry Conduitry added the awaiting submitter needs a reproduction, or clarification label May 29, 2019
@EliSnow
Copy link

EliSnow commented May 30, 2019

@georges-gomes, are you using parcel-plugin-svelte? I ask because I'm using it with parcel and I'm encountering this issue which causes current_component to be undefined in other contexts such as dispatching an event.

@georges-gomes
Copy link
Author

@EliSnow, not using parcel-plugin-svelte but if it calls the compiler the way I do it probably have the same result. I will report a reproduction path and hopefully the fix will serve to both cases 👍

@mrkishi
Copy link
Member

mrkishi commented May 30, 2019

It sounds like another case of multiple svelte/internal modules?

I think we need to look into reworking how svelte/internal keeps track of the current component since it breaks when mixing components not bundled with the app. It sounds like we need to find a way to pass Svelte's internal runtime state when instantiating components, since slots and callbacks end up mixing different svelte/internal together.

@georges-gomes
Copy link
Author

@mrkishi I think you are right.
Here are more details that I found:

Still on the same example:

<svelte:options tag="my-el"/>

<script>
	import { createEventDispatcher } from 'svelte';

	console.log('create dispatch');
	const dispatch = createEventDispatcher();

	function sayHello() {
		console.log('call dispatch');
		dispatch('message', {
			text: 'Hello!'
		});
	}

</script>

<button on:click={sayHello}>
	Click to say hello
</button>

It gets compiled to:

/* module.svelte generated by Svelte v3.4.4 */
import {
	SvelteElement,
	add_location,
	detach,
	element,
	init,
	insert,
	listen,
	noop,
	safe_not_equal
} from "svelte/internal";
import { createEventDispatcher } from "svelte";
...
...
...

current_component is 'saved' with the init function on svelte/internal package.
And createEventDispatcher reads it on svelte package.

Now if you rollup, all methods of svelte/internal and svelte get included and all is working fine.

But in my case, I feed a non-rollup version of the component to the browser and redirect the imports to svelte/internal and svelte packages bundled in ES.

I guess that if I make a mega bundle of svelte/internal and svelte it's going to work because current_component is going to be in the same module for both init() and createEventDispatcher but it's not generic.

If current_component is never used outside of svelte/internal, it will be fine.

@georges-gomes
Copy link
Author

If I use import { createEventDispatcher } from 'svelte/internal'; instead of import { createEventDispatcher } from 'svelte'; then it seems to work because it's loading from the same module.

@georges-gomes
Copy link
Author

the svelte package is a view on svelte/internal

export {
	onMount,
	onDestroy,
	beforeUpdate,
	afterUpdate,
	setContext,
	getContext,
	tick,
	createEventDispatcher
} from 'svelte/internal';

In my scenario all functions requiring current_component fail...
onDestroy, onMount, beforeUpdate, etc...

the ones in `/internal/lifecycle.ts` basically...

@georges-gomes
Copy link
Author

georges-gomes commented May 31, 2019

Hi

Maybe my use cases is a little bit special. But, from a pure theorical Javascript perspective, it means that the js generated with svelte and svelte/internal is not valid. It only works if it's bundled.

Would a replacement of import from svelte on .svelte files be replaces to import from svelte/internal on the generated js? This would generate valid results.

What do you think?

Thanks
Georges

@EliSnow
Copy link

EliSnow commented May 31, 2019

@georges-gomes, thank you! Importing from svelte/internal allows me to workaround this issue.

@georges-gomes
Copy link
Author

@EliSnow all pleasure is mine :)

@georges-gomes
Copy link
Author

@Conduitry what's your view on this? How do you think we should tackle this?

@mrkishi
Copy link
Member

mrkishi commented May 31, 2019

If I understand the problem correctly, just changing the imports to point to svelte/internal isn't enough because they could still point to different packages depending on how your components are bundled. It solved your specific issue, but if you had two completely unrelated Svelte components compiled to vanilla javascript bundled with Svelte, you'd still hit issues with mismatching current_component when using slots or callbacks.

This needs to be solved, but I don't think it's as simple as rewriting imports.

But in my case, I feed a non-rollup version of the component to the browser and redirect the imports to svelte/internal and svelte packages bundled in ES.

How are you doing the redirects, by the way? Because exports from svelte and svelte/internal should always reference the same things, so you shouldn't need to import { createEventDispatcher } from 'svelte/internal' if your module resolution works like regular ES modules.

@georges-gomes
Copy link
Author

georges-gomes commented May 31, 2019

@mrkishi yes you are right, svelte imports svelte/internal
if it had proper ES module relationship it would work.

Doesn't work for me because I bundle svelte and svelte/internal separately (and dumbly) with all dependencies. Everything is bundled twice basically... So the problem might be on my side :D

if you had two completely unrelated Svelte components compiled to vanilla javascript bundled with Svelte, you'd still hit issues with mismatching current_component when using slots or callbacks.

If you bundle the components individually, it would be all good right? but probably a lot of duplicated code...

@mrkishi
Copy link
Member

mrkishi commented May 31, 2019

No, bundling them individually would still throw current_component errors in certain cases. They'd mostly work, until you used a bundle's helper from another. This would be most likely to happen when passing callbacks or using slots from components between different bundles.

In your case, it sounds like you just want to make svelte external when compiling your components and provide svelte's packages as es modules (straight from npm; look at the .mjs files).

@georges-gomes
Copy link
Author

@mrkishi Thanks I will try around these lines
I'm closing the issue until I find something meaningful to report.

@Klustre
Copy link

Klustre commented Jun 27, 2019

I ran into the same issue, although not with custom elements. It happened while using createEventDispatcher in a Card.svelte component in a clone of cep-svelte-starter.
Not sure why this happened as none of the dependencies seem to have changed.

Workarounds:

  1. import { createEventDispatcher } from 'svelte/internal'
    from Custom-element=true issue with dispatch(); #2896 (comment)
  2. "postinstall": "rimraf node_modules/svelte/*.mjs"
    from When calling onMount, afterUpdate, etc. receiving "Uncaught Error: Function called outside component initialization" DeMoorJasper/parcel-plugin-svelte#46 (comment)

Stacktrace:

Uncaught TypeError: Cannot read property '$$' of undefined
at eval (index.js?5043:503)
at HTMLInputElement.dispatchRemove (Card.svelte?292f:11)
at HTMLInputElement.eval (index.mjs?017d:199)

And the corresponding lines:

const callbacks = component.$$.callbacks[type];

return fn.call(this, event);

@nickmanning214
Copy link

I ran into this problem now. I'm trying to make and publish a function that dispatches an event using createEventDispatcher. It's intended to be used on any svelte project. But it's not bundled with svelte components so there is no "current_component". I'm getting Cannot read property '$$' of undefined and the undefined variable is current_component.

@nickmanning214
Copy link

Update: I was advised not to import directly from svelte on a reusable publishable store but instead declare svelte as an external.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
awaiting submitter needs a reproduction, or clarification
Projects
None yet
Development

No branches or pull requests

6 participants