-
-
Notifications
You must be signed in to change notification settings - Fork 4.5k
Allow to return state in *.svelte.js/ts #9965
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
Comments
I had proposed something similar to this before. I think instead of trying make a convention with any function starting with export function func() {
const value = $state(0); // compiled to $.source(0);
// ...
return $box(value); // compiled to just `return value`;
} <script>
import { func } from './file.svelte.ts';
let name = $unbox(func()); // 'name' is handled as a state, as if it had been created with $state()
</script> More verbose, but actually works in more places than just function boundaries (works for global exported state too). Plus it goes nicely with the runes vibe... "to transport your runified state, you must first place it in a magical box" etc. |
@mimbrown Seem a pretty good solution for me. |
@mimbrown, I think your solution is more prone to errors as it depends on the developer remembering to wrap and unwrap the signal, doing it in the correct locations and as exposes the signal to the developer, something the team wants to avoid export function func() {
const value = $state(0);
// ...
return () => value;
} <script>
import { func } from './file.svelte.ts';
let name = $derived.with(func());
</script> @adiguba's solution is better because in addition to enforcing a convention, all the work continues to be done by the compiler, protecting the signal implementation |
You can't assign to derived state though |
Except that's exactly what you're doing, just in a less "blest" way.
The box could be implemented in a way that doesn't expose the signal. Actually it could be implemented to do more or less what you wrote, just more efficiently and with write capabilities. The idea is, normally we want to create state in such a way where we control the modification of the state. But sometimes, we want to create some state and completely delegate the future modifying of it to the outside world, but have some custom logic run in an effect whenever it changes. Like if we want to sync it with local storage, or query parameters, or some third-party service, or write some custom debugger (though |
In your proposal, the developer needs to wrap and unwrap the signal, in @adiguba's proposal you mark the function that returns a signal and the compiler takes care of the wrapping and unwrapping |
That's fair. I meant to respond specifically to the example you had added. Here's my concerns with the original proposal:
export function $func() {
if (someCondition) {
return 5;
}
let myState = $state(0);
return myState;
}
|
Yes, a special code for special treatment...
Like
I's invalid !
Yes it's a problem as it's return an unknown object, with unspecified behavior :/ One solution to this can be to return a specific type defined like this : type BoxedState<T> = {
get(): T;
set(newValue: T);
} So this : export function $func() {
const value = $state(0);
// ...
return value;
} Will be compiled to something like this : export function $func() {
const value = $.source(0);
// ...
return { get: ()=>$.get(value), set: (v) => $.set(value, v) };
} So the returned value can be used can be used in "classic" javascript's code, and enhanced in *.svelte files... |
No, stores allow bringing reactivity to/from third-party libraries while letting them still be mostly framework-agnostic.
Nope, their return types are
There are lots of ways of leaking such a function and thus leaking the raw signal. Preventing all of them will be a huge PITA for the team and an annoying limitation for the users. Boxing the signal means you can feed the svelte code a boxed signal with custom setter/getter logic, which seems the team don't like. BTW, isn't #9951 about the same problem - passing somewhere the state but not its value? I even proposed there an idea that seemed to meet the team's requirements unless I overlooked something. |
Stores allow to bring reactivity to/from third-party libraries, and I think we should have something similar with states. => So how I can pass a state to/from third-party libraries ? And yes, event if at first i was thinking that this issue and #9951 were two separate problems, I think they can have a common solution. Otherwise, your idea is similar to the |
For exemple if I want a state to be backed on a sessionStorage, currently with fine-grained reactivity I can use something like this : // simplified implementation, ignoring parse/format :
export function sessionStorageState(key, initialValue) {
const state = $state({ value : sessionStorage.getItem(key) ?? initialValue} )
$effect( () => {
sessionStorage.getItem(key, state.value);
});
return state;
} But to use it I have to update my code in order to use an object instead of a simple state. <script>
+ import { sessionStorageState} from './storage.svelte.js';
- let count = $state(0);
+ let count = sessionStorageState('key', 0);
function increment() {
- count += 1;
+ count.value += 1;
}
</script>
<button onclick={increment}>
- clicks: {count}
+ clicks: {count.value}
</button> I think it would be better to only have to change the state declaration, and use it directly. |
I tried to export a
Or am I missing something? |
Closing for the reasons given in #9237 (comment) |
Describe the problem
We can use
$state()
in *.svelte.js/ts files, but it's not possible to return the state himself, as the following code will return the value :In order to have a reactive state, we need to encapsulate the state in an objet, like this for example :
Or this :
But all these solution imply that we handle this on the template, with something like
{varName.get()}
or{varName.value}
instead of using directly{value}
like classic $state declared inside<script>
Describe the proposed solution
Allow *.svelte.js/ts file to declare function that return a state.
We can use a name convention and a compiler check for this.
For example function's name prefixed by
$
will return a state, so :The compiler should produce an error if the return of the value is not a state.
This will allow to do this in *.svelte files :
This will allow to create all sort of states, like what you can do with stores, without changing the usage in the template.
For example for a state backed on localstorage :
Alternatives considered
Using current behavior, and changing the template based on how we create the state.
Importance
nice to have
The text was updated successfully, but these errors were encountered: