svelte
svelte copied to clipboard
Allow `svelte/actions` to update passed variables
Describe the problem
<script>
export let bar;
function foo(node, bar) {
bar = 123;
return {
update(bar) {},
destroy() {},
};
}
</script>
<div use:foo={bar}>
{bar}
</div>
Currently this prints undefined
. This makes recreating bind:group
much harder.
Describe the proposed solution
Allow svelte/actions
to update passed variables.
Alternatives considered
The passed object could be a reference.
Importance
would make my life easier
You are shadowing bar
with a local variable / argument. I recommend eslint to catch this https://eslint.org/docs/latest/rules/no-shadow
<script>
export let bar;
function foo(node, _bar) {
bar = 123;
return {
update(bar) {},
destroy() {},
};
}
</script>
<div use:foo={bar}>
{bar}
</div>
This is on purpose. I'd like to update whatever I pass into use:foo
, so it works similarly to bind:this
.
I think I'm understanding what you want to achieve. But I don't see how this could be added in a meaningful way to Svelte. First of all it can only work for the special case of inline-actions, because reactivity can not leak into vanilla JS files. So updating bar
inside an action defined in a different file can never find it's way back to Svelte land (except with a runtime-overhead à la Proxy
). Second it would require some sort of naming convention or intentional abuse of variable shadowing to make it work the way your example shows. It would then also work counter intuitive to how scopes usually work in JavaScript and would make code harder to read (Svelte always follows the principle that code is read way more often than it is written). Which would explain why I had no idea what your code is supposed to do (something that is not vanilla JavaScript).
Maybe there's a way to add this to https://github.com/sveltejs/rfcs/pull/41, which at least has the potential to allow this type of logic.
For completeness sake your code example is very similar to this https://github.com/sveltejs/svelte/issues/7429
This is on purpose. I'd like to update whatever I pass into
use:foo
, so it works similarly tobind:this
.
This would break all javascript semantics and for that matter is impossible for svelte to implement without compiling the action functions which currently doesn't happen. You can write actions in any javascript file and use them in svelte. In order to support this, actions would have to be written in svelte files (which there actually is an RFC for btw) but even if that were to theoretically happen I still don't think is would be remotely easy to implement this kind of api in the compiler side. What svelte wants you to do in this case is use a store. So you should make bar
a store and then inside of your action just do bar.set(newValue)
instead of bar = newValue
.
I do think it would be useful for svelte to get some kind of syntax for converting a reactive local variable to a store similar to how you can go the other way. Like if I have myStore
then to use it as a reactive local variable I can use $myStore
it would be nice if for reactive local variables I had come other syntax to go the other way maybe for myLocalVar
it could be something like _$_myLocalVar
. In this case you would take a store as the argument to foo
and use it as mentioned above (bar.set(newValue)
) but you wouldn't have to make bar
in your component come from a store it could instead just be a local state variable and then when you use it you could do use:foo={_$_bar}
. This to me would address the underlying issue that state is somewhat difficult to share between non svelte and svelte code sometimes. But as of now the solution is just to make your state into a store in the component that way you can share its reactivity outside. You could also do the whole _$_var
for store thing yourself for instance:
<script>
import {writable} from "svelte";
let myValue = 5;
let _$_myValue = writable();
$: _$_myValue.set(myValue);
</script>
<div use:foo={_$_myValue} />
this could be useful if myValue
was a prop or maybe a field on an object for instance. Although in most cases I think you should just make myValue
itself a store.
I have run into this problem before when using svelte as well and I think the general solution is to just make things into stores.
You can pass callback to action and update it in callback, or use object instead primitive.
<div use:foo={(v) => bar = v}>
{bar}
</div>