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

How can I use this with Svelte 5 runes? #54

Closed
kristianmandrup opened this issue Jul 26, 2024 · 6 comments
Closed

How can I use this with Svelte 5 runes? #54

kristianmandrup opened this issue Jul 26, 2024 · 6 comments
Labels
question Further information is requested seen I've seen and read this issue and I'll try find some time soon to work on it.

Comments

@kristianmandrup
Copy link

I would like aggregate the SSE events in a messages state/store. Would I run the source in an onMount or $effect?
It is unclear how to use it with stores

	const connection = source('/events/app');
	const channel = connection.select('project');
<script>
	let messages = $state<string[]>([]);

	messageStore.addMessage($json);

	messageStore.subscribe((value) => {
	 	messages = value;
	 });
</script>

<div>
	<h1>Projects</h1>
	<h2>Messages</h2>
	{#if messages.length === 0}
		<p>No messages yet.</p>
	{:else}
		<ul>
			{#each messages as message}
				<li>{message}</li>
			{/each}
		</ul>
	{/if}
</div>
@razshare razshare added the seen I've seen and read this issue and I'll try find some time soon to work on it. label Jul 26, 2024
@razshare
Copy link
Owner

razshare commented Jul 27, 2024

Hello @kristianmandrup , I'm not very familiar with Svelte 5 and I plan to wait until it's actually released to do any work specific to it.

That being said, in Svelte 4 you would just have to do this

<script>
  import { source } from 'sveltekit-sse'
  import { messageStore } from 'message-store'
  const connection = source('/events/app')
  const message = connection.select('message')
  $: messageStore.addMessage($message)
</script>

// Your markup goes here.

Because .select() gives you a store itself, hence the $message in the code above.

I'm assuming the equivalent in Svelte 5 is something like this

<script>
  import { source } from 'sveltekit-sse'
  import { messageStore } from 'message-store'
  const connection = source('/events/app')
  const message = connection.select('message')
  $effect(function run() {
    messageStore.addMessage($message)
  })
</script>

// Your markup goes here.

I haven't tested it though.
I do remember the Svelte team saying at some point that stores will interact with runes as they normally interact with the current reactive system in Svelte 4, so it would be a breaking change on their part if this doesn't work.

Edit: from a quick search I found this https://svelte-5-preview.vercel.app/docs/faq#breaking-changes-and-migration
image


Let me know if this answers your question.

@razshare razshare added the question Further information is requested label Jul 27, 2024
@kristianmandrup
Copy link
Author

Thanks a lot for your answer. For now I seem to have it working using the following pattern:

	const transformed = channel.transform(function run(data) {
		if (data === '') return;
		// TODO: parse json
		return `${data}`;
	});

	let projects = $state<ProjectPayload[]>([]);
	let lastMessage = $state<string>('');

	transformed.subscribe((value: string) => {
		if (!value) return;
		lastMessage = value;
                // TODO: move to transform function
		const json = JSON.parse(value);
		const { source, model, action, data } = json;
		if (model !== 'project') {
			console.log('not a project event');
			return;
		}
		const project = data;
		// TODO: depending on the event, add, remove or update the project
		projects = [...projects, project];
	});

	import { getToastState } from '$lib/toast-state.svelte';

	const toastState = getToastState();

	const toastMap = new Map<string, unknown>();

	$effect(() => {
		const message = lastMessage;
		if (!message) return;

		const json = JSON.parse(message);
		const { data } = json;
		if (!data) {
			console.error('missing data', json);
		}
		const { name, description } = data;
		if (!name) {
			console.log('missing name', data);
			return;
		}
		// already processed
		if (toastMap.get(name)) {
			console.log('already made toast for', name);
			return;
		}
		toastMap.set(name, data);
		toastState.add(name, description);
	});

Currently overly verbose and complicated, but it gets the job done. The key was to simply subscribe to the readvalue store returned by transform

transformed.subscribe((value: string) => { ... })

Then from there I could set a $state with the value and work from there. I'm sure there is a much simpler way.
Btw, I had an issue with the first message received from SSE always being an empty string for some reason, but it might well be an issue with my internal logic, such as undefined being sent as empty string?

@razshare
Copy link
Owner

razshare commented Jul 27, 2024

Btw, I had an issue with the first message received from SSE always being an empty string for some reason, but it might well be an issue with my internal logic, such as undefined being sent as empty string?

That is expected behavior, the first value is always empty because it takes time to actually open the connection to the server.

It's either that or force userland to deal with a Promise<Readable<string>> and {#await} in the markup.

A third solution would be initializing the value in SSR but that would mean your server would have to open a connection to itself to read the value, which is a waste of resources and will probably slow down the SSR itself by a lot, especially in a cloud environment where you're not 100% the node doing the SSR will actually connect to itself. It could connect to a different twin node and slow down things even more.

@WarningImHack3r
Copy link

Any update on a new major version that uses runes instead of stores? It's not blocking but it'll be a great improvement

@razshare
Copy link
Owner

Hello @WarningImHack3r ,

I've had a few attempts at trying to convert the usage of stores into runes since Svelte 5 came out.

I wasn't really happy with the outcome, specifically the api is quite more clunky when it comes down to observing new incoming data outside of the Svelte Template.

To achieve the same results it is required to use the $effect rune, which ironically enough, the Svelte Team discourages using.

Just because you reminded me of this, I'll give it another shot this weekend, I'll try come up with something, but stores are pretty damn good at this type of stuff, so it'll be hard to come up with something better, imo.

I'll update you, have a good day.

@WarningImHack3r
Copy link

@razshare thanks for your reply!
$effect are indeed discouraged, but not prohibited, it's fine if you don't have any other way ^^
Stores are good but will come to an end in the next Svelte majors (we have still the time to see it coming though), and are also far less granular and efficient than runes!

Good luck with your attempts, take your time!

(side-note: I think this issue should be closed as "won't fix"/"not planned" instead of "solved" for the time being, even though it'll eventually be solved when you drop a rune-powered version)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
question Further information is requested seen I've seen and read this issue and I'll try find some time soon to work on it.
Projects
None yet
Development

No branches or pull requests

3 participants