sveltejs / svelte

Cybernetically enhanced web apps
https://svelte.dev
MIT License
76.98k stars 4k forks source link

Add $readable and $writable runes #9237

Closed vd-aloker closed 1 month ago

vd-aloker commented 8 months ago

Describe the problem

With stores, it's easy to pass a store to a function and have the function read or write its value:

function doSomething(theStore){
  const current = get(theStore);
  // ...
  theStore.set('New value')
}

let val = writable('')
doSomething(val)

With state runes, it's not as easy, because the underlying signal is not exposed. Now I'd have to pass in an object that wraps access to the state:

export function doSomethingWithState(state){
  const current = state.value;
  // ...
  state.value = 'New Value'
}

let val2 = $state('');

doSomethingWithState({ 
    get value() { return val2}, 
    set value(newValue) { val2 = newValue}
})

As far as I can see, there's no way to create a helper function or similar that could simplify this. As a result, I expect more boilerplate code when using state as a replacement for stores.

Describe the proposed solution

Given that it's not possible to create a helper function that creates the wrapper object, maybe it could prove useful to have $readable and $writable runes that cause the compiler to create those wrappers. $writable would add the setter, $readable would omit it.

let val2 = $state('');
const wrapped = $writable(val2)
doSomethingWithState(wrapped)

This gets compiled to

let val2 = $state('');
const wrapped = { 
  get value(){ return val2; }, 
  set value(newValue){ val2 = newValue }
};
doSomethingWithState(wrapped)

($readable would omit the setter)

Obviously, this is all pretty new to me, so maybe I'm missing something.

Alternatives considered

The alternative is creating the object with the getter/setter manually.

Importance

would make my life easier

look997 commented 8 months ago

This can be hidden in $state.writable() and $state.readable(). There is $effect.pre(), so it is similar....

aloker commented 8 months ago

As I learned, the way forward will probably be something like a helper function such as

function writable(initial){
  let value = $state(initial);
  return {
    get value() { return value; },
    set value(v) { value = v; }
  }
}

which can be implemented in user land or as a helper function shipped with Svelte. You'd lose immediate access to the value, but IMO fewer runes is better. As such, I think, this feature request is obsolete.

WaltzingPenguin commented 8 months ago

While you can implement it in userland, that means everyone will implement a slightly different version. For library authors, that's a big deal. If you're using multiple third party libraries, how much of a headache do you want to deal with because one used object.value, one used object.val, and the last one decided on object.$?

aradalvand commented 8 months ago

As such, I think, this feature request is obsolete.

No really. This feature request is (or should be) about shipping this helper with Svelte, because it's such a commonly-needed utility, it makes sense to include it in the standard library.

aloker commented 8 months ago

Helper functions as outlined above should be shipped with Svelte, but I'm not sure anymore (I'm the author of the issue, company account vs private account etc) that there's a need for runes for this purpose. Fewer runes means less compiler magic which is better IMO.

stalkerg commented 8 months ago

Honestly, I believe removing store and $storeVar syntax is a mistake. It seems like the core team keeps in mind only a few use cases and completely ignores others. Runes itself is fine, but we should keep the store as is - runes and signals are not a replacement for the store for all cases.

Rich-Harris commented 8 months ago

Perhaps you'd like to enumerate those cases?

stalkerg commented 8 months ago

Yes, I will try. I hope the community also helped me.

PS Maybe it's just my common overreaction... as I remember, I argue a lot after svelte2 and after svelt3 release as well. :) Sory for that, but I feel there is something wrong.

JakeBeaver commented 8 months ago

I'm hoping we get a LOT of syntax sugar before runes ship. Runes did make global state a little more manageable and more fine grain for when you need it, but they effectively nuked the .svelte files (yes, you did blow it all to hell :D).

Would love to see something like https://pelte.dev/ be included out of the box.

I almost never need more than simple variables in components, so I'm dreading the day when the automatic migration to runes comes along with svelte 6+, (effectively runs pelte-ish preprocessor as a converter?), makes all reactive variable declarations double in length, and thus my codebases becomes so much less lovely and svelte.

adminy commented 8 months ago

write the value out at the end of your function calls, this way you do all the distructive stuff at the end or all the reactive stuff outside your core logic. its even better this way as it forces you to keep your code cleaner and at the boundaries of state interactions.

stalkerg commented 7 months ago

I'm hoping we get a LOT of syntax sugar before runes ship.

I am hopeful too but core developers are silent and even officially said, that it's maybe will not be part of the first release.

stalkerg commented 3 months ago

I suppose $wrap like function should be part of svelte5 https://github.com/flakolefluk/dark-runes?tab=readme-ov-file#wrap-writables-readables-and-properties

Rich-Harris commented 1 month ago

Circling back: it would be premature to add these until Svelte 5 is stable and we see what patterns emerge organically. It's easy enough to do this...

const answer = $state({ value: 42 });

...for the cases where you need this (which I honestly don't expect to be all that frequent), that introducing additional API (whether in the form of a rune or a helper) feels excessive. (That wasn't possible at the time this issue was created, since it predates #9739.)

It may well be the case that we need to pave some cowpaths in future, but the sensible thing to do for now is wait.

stalkerg commented 2 weeks ago

Thanks, seems it's a good enough for me. Yeah, proxied state it's big improvement.