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

Share/Inject functions and object instances in universal loaders without globals/singletons #12455

Open
Rican7 opened this issue Jul 9, 2024 · 4 comments
Labels
feature request New feature or request

Comments

@Rican7
Copy link

Rican7 commented Jul 9, 2024

Describe the problem

When working with universal loaders (+page.js and +layout.js), there's (seemingly) no way to pass around functions or object instances without resorting to globals or singletons.

On the server-side, you can initialize objects in hooks.server.js and then pass them to server loaders via event.locals (which has it's own oddities of naming/documentation), but there doesn't seem to be an analogous way to pass these kinds of things in a client or universal context.

In components I can use stores/context, or just drill props, but there just doesn't seem to be a way to easily share complex or expensive to initialize objects in client/universal loaders without using globals/singletons.... which comes with all of the negatives of using globals.

Describe the proposed solution

I'd like to, generally, have a way to define a init function (or constructor?) or some way to define dependencies in loaders that isn't just importing globals.

If that's not something that is to be considered, then I'd like to at least see a way for client/universal loaders to share complex data that's similar to the server loaders capabilities.

Alternatives considered

Just using globals/singletons... but that complicates testing and comes with all of the drawbacks of globals/singletons.

Importance

would make my life easier

Additional Information

This may be similar to #6714 or #7107... but from a different functional perspective.

@david-plugge
Copy link
Contributor

event.parent is the intended way of doing this, but it is a little annoying as it results in a waterfall. I proposed event.locals for shared load functions and a shared handleLoad hook some time ago. Would be awesome to get this feature for spas!

@Rican7
Copy link
Author

Rican7 commented Jul 9, 2024

event.parent is the intended way of doing this, but it is a little annoying as it results in a waterfall.

Yea, that causes waterfalls and it has to be initiated in a top-level layout or something, which is inconsistent with the way it works with a server (using hooks).

I proposed event.locals for shared load functions and a shared handleLoad hook some time ago. Would be awesome to get this feature for spas!

Yea, honestly that would make things more consistent with how it works for the server. Which, again as I said still isn't the clearest mechanism, but at least that would be consistent. The lack of common "entry-point" or "main" for an SK app makes this all a bit confusing and hard to discover.

@eltigerchino eltigerchino added the feature request New feature or request label Jul 10, 2024
@Crisfole
Copy link
Contributor

Moving conversation from here:

https://discord.com/channels/457912077277855764/1287826942376149088

This particular request is basically the same as my question in the above discord chat.

I have a Supabase client that I initialize in my root layout:

src/routes/+layout.server.ts

export const load = async ({ locals: { session, user } }) => {
	return {
		session,
	};
};

src/routes/+layout.ts

import { PUBLIC_SUPABASE_ANON_KEY, PUBLIC_SUPABASE_URL } from "$env/static/public";
import { combineChunks, createBrowserClient, isBrowser, parse } from "@supabase/ssr";

export const load = async ({ fetch, data, depends }) => {
	depends("supabase:auth");

	const supabase = createBrowserClient(
		PUBLIC_SUPABASE_URL,
		PUBLIC_SUPABASE_ANON_KEY,
		{
			global: { fetch },
			cookies: {
				get(key) {
					if (!isBrowser()) {
						return JSON.stringify(data.unvalidated_session);
					}
					const cookie = combineChunks(key, (name) => {
						const cookies = parse(document.cookie);
						return cookies[name];
					});
					return cookie;
				},
			},
		},
	);

	return { supabase };
};

I have nested layouts. In order to re use this client anywhere other than a component (which is not the 'blessed' Svelte Kit way to load data) I have to await parent() which introduces wholly unnecessary waterfalls. The other alternatives seem to be re-creating the client in every downstream layout or abandoning universal loaders.

I'd love to see the client equivalent of hooks. Or (maybe better?) a way to introduce specific parent dependencies:

// Where the parent is the value from `event.route.id`
const { supabase } = await parent("/");

This would allow introducing waterfalls that are needed without forcing it to be strictly sequential.

@david-plugge
Copy link
Contributor

I worked out a solution for universal load functions: https://gist.github.com/david-plugge/33cd821a4c829059f67ce18468b8dfca.
The same would work for server load functions aswell. Its definately a hack but still better than a waterfall. This should be built into sveltekit.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature request New feature or request
Projects
None yet
Development

No branches or pull requests

4 participants