-
-
Notifications
You must be signed in to change notification settings - Fork 4.3k
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
Unable to use return in reactive expression #2828
Comments
Hi @taylorzane! You can make the if statement reactive directly if you prefer. $: if (count <= 10) {
bar = 'bar' + foo
} |
Hey @EmilTholin, I appreciate the advice. I know I could rewrite the expression as a whole to suit the existing syntax, and perhaps this is a |
Labels not being treated as a function by the compiler comes back to bite me a lot since I prefer to use early-outs whenever possible. $: {
if(!case) {
return;
}
doWork(case);
} This case can obviously become a simple |
👆 That right there is exactly why I'd like support for this. It also keeps nesting at one level lower than using a wrapping if/else. |
Yeah, I can see the appeal, but I would personally rather opt for using an IIFE than start treating blocks as functions. $: (() => {
if (count > 10) {
return
}
bar = 'bar' + foo
})(); |
I think in my ideal world the svelte compiler would be able to see that pattern and remove it to keep the code streamlined, paying the cost for IIFE every time a reactive statement runs isn't something I'm super comfortable with. |
A possibility would be to have the compiler interpret a bare function following the $: () => {
// this could be interpreted as an IIFE instead of a no-op: "anonymous reactive function"
} There's tension between the code you'd ideally write (returns in blocks with compiler smarts) versus code that matches expectations of JS and works better with e.g. TypeScript. I'm curious what Svelte's philosophy is here, what costs will be paid to write more ideal code. Elm is a good example of a language+framework that pays seemingly anything to get to its ideal form. |
This is a little trickier than just deciding to allow let count = 0
$: {
if (count > 10) return;
bar = 'bar' + foo
}
$: somethingElse = bar + 'whee'; ...Svelte generates this: $$self.$$.update = ($$dirty = { count: 1, foo: 1, bar: 1 }) => {
if ($$dirty.count || $$dirty.foo) { {
if (count > 10) return;
$$invalidate('bar', bar = 'bar' + foo)
} }
if ($$dirty.bar) { $$invalidate('somethingElse', somethingElse = bar + 'whee'); }
}; In other words it would fail to update These aren't intractable problems, but it's definitely not something that's straightforward to fix. @tivac's suggestion is the one I like the most — Svelte could generate this from that example: function $$update_bar() {
if (count > 10) {
return
}
$$invalidate('bar', bar = 'bar' + foo);
}
$$self.$$.update = ($$dirty = { count: 1, foo: 1, bar: 1 }) => {
if ($$dirty.count || $$dirty.foo) $$update_bar();
}; |
It's not valid JavaScript so it would break prettier and other tools. 🤔 |
Unless the benefit is proven to be very substantial, I don’t think additional creative interpretations of JS semantics will be good for Svelte. I object to it being included on a whim. I’m afraid that this stuff will pile up and lead to unexpected issues in the future. Using an IIFE as @EmilTholin suggested looks like a good choice. I see at least three solutions to @tivac ‘s performance objection to it:
|
It seems like there's a decent amount of overhead for an IIFE compared to no IIFE (see benchmark). Obviously that's a very naïve test and not indicative of actual usage in a Svelte component. Personally, I have no preference as long as we can get some sort of bail early functionality. |
Svelte supports IIFEs already, they just don't look great - https://svelte.dev/repl/99fd078d1bbd46088bcd6fc59e761d4d?version=3.4.4 |
Yeah, they do work but they're not handled specially so every time they run they're recreated. 😑 |
Something just occurred to me that maybe should be obvious. Svelte is repurposing labeled blocks, by deleting the actual label and moving the block into a function. The only reason someone might expect However, a real labeled block already has a way to exit early. In fact, it's pretty much the only "feature" a real labeled block has: $: {
if (count > 10) {
break $;
}
bar = 'bar' + foo
} That's valid JS, but if you put that into the Svelte compiler, it just looks at you funny. It happily accepts the following though: X: {
if (count > 10) {
break X;
}
bar = 'bar' + foo
} ...but of course doesn't make it reactive. So, if early exits are even a concern, my suggestion would be to implement it by adding support for labeled breaks. That would bring Svelte semantically closer to "real" JS in its handling of labeled blocks, rather than continue to deviate. It also solves the issue with returns and multiple reactive statements. I'd even imagine it would be quite straightforward to implement. The compiler would simply not delete the label when it moves the reactive declaration into the update function. Example change: $$self.$$.update = ($$dirty = { count: 1, foo: 1 }) => {
- if ($$dirty.count || $$dirty.foo) { {
+ if ($$dirty.count || $$dirty.foo) { $: {
if (count > 10) {
break $;
}
$$invalidate('bar', bar = 'bar' + foo)
} }
}; |
This isn't pretty, but a workaround that you can do at the moment is to put a label statement in the reactive statement. It's less code than an iife anyway. The REPL will let you get away with the following:
This always bails out early. The count never gets set back to nine. |
Hilariously, the above hack will also likely be backwards compatible if @trbrc 's solution is adopted because labels can be chained together. The following also works:
|
(Unless it's changed again!) this will be resolved by #3539 by letting you |
It turns out that this is resolved for browser builds by #3539, but it's not yet working for SSR builds. The |
Do we have anything on the |
I too would find it useful to be able to do early returns or breaking out of the labeled reactive blocks. It helps with not nesting the code too much. Benchmark link from @taylorzane is broken, could anyone please clarify if a dummy function such as:
has different or same performance as: $: if (reactiveVar) {
} or would there be any consequences? |
When using a
return
statement in a reactive expression/block an error is thrown:'return' outside of function (L:C)
Since the reactive expression contents is wrapper in a function at compile time, this shouldn't cause any syntax errors at runtime.
I imagine this will require changes to the svelte compiler and also the eslint plugin rules.
See a simple repro here:
https://svelte.dev/repl/8ca16f5088c34dd89574a064868d9b4d?version=3.4.1
The text was updated successfully, but these errors were encountered: