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

Allow svelte/actions to update passed variables #8959

Closed
szmarczak opened this issue Jul 12, 2023 · 6 comments
Closed

Allow svelte/actions to update passed variables #8959

szmarczak opened this issue Jul 12, 2023 · 6 comments

Comments

@szmarczak
Copy link

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

@Prinzhorn
Copy link
Contributor

Prinzhorn commented Jul 12, 2023

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>

@szmarczak
Copy link
Author

This is on purpose. I'd like to update whatever I pass into use:foo, so it works similarly to bind:this.

@Prinzhorn
Copy link
Contributor

Prinzhorn commented Jul 12, 2023

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 sveltejs/rfcs#41, which at least has the potential to allow this type of logic.

@Prinzhorn
Copy link
Contributor

For completeness sake your code example is very similar to this #7429

@Zachiah
Copy link
Contributor

Zachiah commented Jul 25, 2023

This is on purpose. I'd like to update whatever I pass into use:foo, so it works similarly to bind: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.

@StagnantIce
Copy link

You can pass callback to action and update it in callback, or use object instead primitive.

<div use:foo={(v) => bar = v}>
	{bar}
</div>

@Conduitry Conduitry closed this as not planned Won't fix, can't repro, duplicate, stale Aug 10, 2023
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

5 participants