sveltejs / svelte

web development for the rest of us
https://svelte.dev
MIT License
79.96k stars 4.25k forks source link

Using the context API in standalone components loaded at runtime returns undefined #3615

Closed ghost closed 7 months ago

ghost commented 5 years ago

Edit: I've found this issue too, which I think looks like the same problem I'm describing here: https://github.com/sveltejs/svelte/issues/3422

Describe the bug I'm trying to build a system that loads external standalone components at runtime, and I want to use the getContext and setContext functions to share a store between all of the components.

I'm using webpack to build the main application which contains an App component that serves as the root of the application, and then I'm using a completely separate project to build another external standalone component (both an SSR version and a DOM versions), which I then load in at runtime using require on the Node server-side, and a <script> tag on the client-side within the main App component.

The external standalone component does load and render correctly, however within the component any calls to getContext return undefined.

I'm not familiar with the Svelte codebase, but I assume this is due to the standalone component being built as a separate project and getting its own versions of the context related variables/functions at build time, rather than sharing the same variables etc. from the parent.

I assume this is a bug, but if I'm just doing something wrong please let me know.

Logs No logs other than getContext printing undefined in the child standalone component.

To Reproduce

Expected behavior I expect to see getContext in the standalone component return data from the same context as the parent component from the main application.

Information about your Svelte project:

Severity I can get around this issue by passing the store I want to share as a prop to the child standalone component, but it's not ideal, especially in large projects where the same store may need to be shared amonst hundreds of standalone components.

This problem is more one of code maintenance/tech debt in my specific case, so I'd much prefer to have getContext work correctly where the standalone child component should inherit the context from the parent component in the main application.

Although in my specifc case I can easily work around the issue using props instead, I can imagine that for applications sharing much more than a simple store this could be prohibitive, so I'd say it's a fairly severe issue.

Additional context I've tried various build options using Webpack, such as changing libraryTarget, or changing Svelte build flags, like changing customElements to true or false.

No matter the build options this problem always seems to occur.

Conduitry commented 5 years ago

Yeah, this is because things are being built with different copies of Svelte's internal runtime. The parent-child relationship isn't correctly maintained between components that are compiled and bundled separately, and so the context is seen as undefined.

The proper way to go about this would be to use the svelte key in the child components' package.json files to point to the original .svelte source and specify 'svelte' in your mainFields webpack config (see https://github.com/sveltejs/svelte-loader). This way all of the components are compiled and bundled together, with a single copy of the internals.

It's probably be possible to make this work even when bundling against different copies of the internals (by making the internals use special global variables to store this, rather than variables local to the module), but I don't think this is a use we want to cater to. There are a number of reasons to just have one copy of the Svelte internals. (Both bundle size concerns, and several features that simply won't work otherwise.) So I would consider this more of a gap in the docs (Questions related to this have come up several times.) than a bug in the code.

ghost commented 5 years ago

That makes sense, and thank you for the explanation. I had a feeling this would be the issue.

Unfortunately in my use case I can't bundle all components up at the same time because I don't always know which components will exist on the site, so I will look for other ways around this if the props route becomes too unmaintainable.

In the case of the browser it's simple, because I can just attach the store to the window instead of using the context API. My only reason for trying to use the context API was because I needed a way to share a new store for each new request on the server-side for SSR.

At first I though that maybe there would be a way to explicitly pass a context to a child component, but if this is a use case you may not want to cater to then I suppose there would be no reason to allow for that to happen in the first place.

ghost commented 5 years ago

This is solved by https://github.com/sveltejs/svelte/issues/3671#issuecomment-541277465

I'll leave this issue open since it's got the docs tag, but I just wanted to point out the comment that solved the problem for me as that's likely the best thing to document.

stale[bot] commented 2 years ago

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

Rich-Harris commented 7 months ago

Closing — as noted, the solution here is to avoid prebundling Svelte (not just svelte/internal, which no longer exists in Svelte 5, but all svelte/* imports)