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

Svelte 5: The $proxy rune #9998

Open
aradalvand opened this issue Dec 25, 2023 · 9 comments
Open

Svelte 5: The $proxy rune #9998

aradalvand opened this issue Dec 25, 2023 · 9 comments

Comments

@aradalvand
Copy link

aradalvand commented Dec 25, 2023

Describe the problem

Basically #3937 (comment) — addresses #3937

Describe the proposed solution

See #3937 (comment) (but as a rune)

Alternatives considered

Currently no sane workaround. It's difficult to overstate the utility of this; it's one of the things Svelte is lacking most severely, IMO.

Importance

nice to have

@brunnerh
Copy link
Member

Related:

@dummdidumm
Copy link
Member

dummdidumm commented Jan 4, 2024

I'm not sure we need input modifiers nor a $proxy rune since you can achieve the same using $derived and getters/setters you can achieve the same using getters/setters. Given that you don't need this often, it doesn't feel worth it to bloat the API surface for this

@Prinzhorn
Copy link
Contributor

Prinzhorn commented Jan 4, 2024

@dummdidumm I'm going to reply here to not spam so many notifications

How would this work without bind?

What do you mean by that?

$proxy has nothing to do with bind per say, it's a two-way derived, no matter how/who writes/reads from it.

In your example this needs to work to do what $proxy is supposed to do:

<button onclick={() => proxy.value = 'test'}></button>

@dummdidumm
Copy link
Member

I see - yes. In other words, if we allow derivedValue.property = newValue, then that works, and that would suffice the $proxy requirements. It would mean you have to create an object, not a simple value, which for now is an acceptable tradeoff to me as pointed out above.

@Prinzhorn
Copy link
Contributor

I agree, this would essentially do what we're asking for with minimal overhead ($proxy would almost have an identical API). I haven't really looked into Svelte 5 yet, so there might be some funny edge cases. But if you can allow writing to derived (without summoning some sort of demon) then for me this would solve my problems. I also kind of like that you explicitly have to do proxy.value instead of just value. It takes away the magic transparency of $proxy but makes it more explicit (you always know it's a proxied value without looking at where it is defined, assuming people follow some naming convention). The downside is that this give you way more control for side-effects (you can access everything that is in scope), while $proxy would marry exactly one $state to a get/set pair. But this can also be a great thing, as you might need to split/merge two values (e.g. you could store day/month/year in three integers but concat them for your date input). I really tend to write a lot when I could've just 👍 😄

So that we're on the same page:

We're asking for:

<script>
	let { value } = $props();
	let proxiedValue = $proxy(value, { get(v) { return v.toUpperCase() }, set(v) { return v.toLowerCase() } });
</script>

<input bind:value={proxiedValue} />
<button onclick={() => proxiedValue = 'test'}>Test</button

We're getting:

<script>
	let { value } = $props();
	let proxy = $derived({ get value() { return value.toUpperCase() }, set value(val) { value = val.toLowerCase() } });
</script>

<input bind:value={proxy.value} />
<button onclick={() => proxy.value = 'test'}>Test</button

without summoning some sort of demon

I mean, do we really want to allow side-effects of mutating objects created via $derived? 🤔 🤔 🤔

@dummdidumm
Copy link
Member

dummdidumm commented Jan 4, 2024

You know what, I'm stupid - we don't even need $derived here, you can just create the object without it.

@Prinzhorn
Copy link
Contributor

Crazy how nature do that

@sbatial
Copy link

sbatial commented May 4, 2024

You know what, I'm stupid - we don't even need $derived here, you can just create the object without it.

That... that actually already works in Svelte today?!?
I mean... I'm not really that suprised..
But I had NO idea that it was possible to have this kind of value conversion... This makes some things probably so much easier....

thanks ❤️ @dummdidumm

@rgon
Copy link
Contributor

rgon commented Oct 19, 2024

we don't even need $derived here, you can just create the object without it.

The first issue with the get/set approach is that we lose all reactivity to object mutations REPL.

So to make it work with objects, you'd at least need 1 getter, 1 setter per field and a modifier. Which would be fine, except that now, we've created a loop setter -> externalValue -> getter, so we cannot keep more state internally than we export. Here's a simple example: we're losing the casing information of our text input REPL

This quickly gets incapacitating if you have something like a File[] input. You may want the Blob to the file data internally, but only export the uploaded file URLs to your parent. Also, you need the value to be bindable so that you can use the component for editing uploaded files. Non-working REPL

Another example is this optimistic upload #12545 (comment). Extending the REPL from this mentioned comment and exporting a $bindable messages field is a nightmare. And I can see this happening in any CRUD app's forms!

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

6 participants