Skip to content

Svelte 5 runes documentation -- add more explanation about having to use classes rather than pojos for "composite" state #9971

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

Closed
cdcarson opened this issue Dec 20, 2023 · 3 comments

Comments

@cdcarson
Copy link

cdcarson commented Dec 20, 2023

Describe the problem

Sorry about the goofy issue title. What I mean is this:

// src/lib/api.svelte.ts

export type Example = {
  value: number;
  doubled: number;
}

// Doesn't work! (returns a pojo)
export const getExampleRunes = (initialValue: number): Example => {
  const value = $state(initialValue);
  const doubled = $derived(value * 2);
  return {value, doubled}
}

// Works!
export class ExampleRunes implements Example {
  value = $state(0);
  doubled = $derived(this.value * 2)
  constructor(initialValue: number) {
    this.value = initialValue;
  }
}

Context: I was converting a function that created a hash of Svelte stores to runes, and missed the implication of the note on the docs page that says:

In this example, the compiler transforms done and text into get/set methods on the class prototype referencing private fields

So I spent a while wondering why the pojo returned from getExampleRunes above wasn't working.

Describe the proposed solution

Consider adding another sentence to that note, something like...

Important: You must define a class for this to work. Svelte can't do this for plain javascript objects.

Alternatives considered

I'm fine with using classes. It's just a documentation issue.

Importance

nice to have

@brunnerh
Copy link
Member

brunnerh commented Dec 20, 2023

Instead of going the classes route, maybe regular $state could benefit from more interoperability with $derived.

Note that the following works:

export const getExampleRunes = (initialValue: number): Example => {
	const example = $state({
		value: initialValue,
		get doubled() { return this.value * 2 },
	});

	return example; // <- currently causes a warning, though
}

This is also what I would just use in most cases.
The only real advantage of $derived in this scenario is, that it provides caching.

Now, if you want to add the value as a $derived, I see no simple way to do that, except splitting things up again. At that point we are back at defining getters/setters for everything manually or switching to a class, which has all the annoying downsides that come with classes (serializability issues, this everywhere, etc.).

To me, this would be the ideal:

export const getExampleRunes = (initialValue: number): Example => {
	return $state({
		value: initialValue,
		doubled: $derived(this.value * 2),
	});
}

But I think there are issues with the this, so the syntax probably is not feasible for the default tooling...

@mstachowiak
Copy link

Take a look at #9739 and the changes coming on this front.

@cdcarson
Copy link
Author

@mstachowiak thanks. I'll try following that example.

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

No branches or pull requests

3 participants