-
-
Notifications
You must be signed in to change notification settings - Fork 4.5k
Passing reactive values to functions #12800
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
Svelte is a compiler, if anything it should just transform a regular call: $use(createCounter(step)); The proposed API might make sense for read-only reactivity, but if the function needs to be able to set a passed value, you end up with some weird magic. const createCounter = $hook((step = 1) => {
// ...
step = 2; // somehow has an effect on the outside
}); To not accidentally cause side effects, the two-way parameters maybe should also require const createCounter = $hook((step = $bindable(1)) => { ... }); The name probably should be different, it does not hook into anything and there are already too many people proclaiming Svelte 5 to just be React. Note that you can already use <script>
let text = $state('Hello');
const sayHello = (element, params) => {
$effect(() => {
element.innerText = params.text;
});
}
</script>
<button type="button" onclick={() => text = 'Bonjour'}>Say Bonjour</button>
<div use:sayHello={{ get text() { return text } }}></div> |
Ekk. I don't know what happened there. I tried to delete the spam comment and it seems to have deleted your comment to @qwuide. I apologise for that. As for this feature request – this is something we've already looked into extensively before and we ultimately felt that this wasn't necessary right now, and introducing it brought about more confusion than it was worth. |
No worries!
Fair enough. In the meantime, is there a recommended way to solve this kind of thing that you suggests over others? |
For now getters and setters are the way to go. You can also have a look at the discussion in #11380 for alternatives |
Yep - just pass |
Describe the problem
Currently, to pass reactive values to functions or class instances, you have to either:
Wrap the function or class instance in
$derived
(which means it will be re-created whenever the values change),Do it imperatively by exposing setters and syncing changes with
$effect
.Pass the values as function getters or box objects.
Wrapping the function in
$derived
allows you to keep the function’s implementation unchanged, use the argument values directly without any unwrapping, and destruct the functions return value while keeping the reactivity. However, the function will be re-created every time the values change, which may not be so performant.Exposing setters on the other hand, is decent in a way that it’s very explicit what’s going on but it will require boilerplate code and must rely on
$effect
to sync state in a way that doesn’t feel quite right.Passing function getters or box objects might not seem like a big deal, especially in simple cases, and TypeScript make this easier. But, in my experience working on and maintaining a large Vue codebase, where much logic resides in composables, this eventually starts to clutter your code to that extent that becomes harder to reason about as it forces you to constantly wrap and unwrap values back and forth and think about how data is passed around. It simply feels very unnatural. This in contrast with React, where you can pass values across function boundaries and it will just work:
Describe the proposed solution
For Svelte, I believe this could be made more effectively by introducing a rune for defining “hooks” or "composables," which would handle keeping values reactive across function boundaries behind the scenes. Similar to
$derived
, it could also support destructuring of the return value.Consider this:
This approach could potentially be applied to element actions as well, making the
update()
hook redundant, as you could instead use$effect
:Now, I have no idea if it's difficult to implement something like this, or if there's something I haven't considered that makes it less ideal. I also don't have a solution for how to combine this with class instances. However, having the constraint applied once for the function and once when invoking it — rather than constantly dealing with wrapping and unwrapping argument values — will lead to cleaner code and a more straightforward developer experience, in my opinion.
Importance
would make my life easier
The text was updated successfully, but these errors were encountered: