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

Bug SSR "Cannot access 'x' before initialization" with reactive let + autosubscribe #3582

Closed
rixo opened this issue Sep 18, 2019 · 15 comments
Closed
Assignees
Labels

Comments

@rixo
Copy link
Contributor

rixo commented Sep 18, 2019

Describe the bug

With SSR, this code:

<script>
  $: x = {}
</script>
{$x}

compiles to this:

// ...
  $x = get_store_value(x);

  let x = {}
// ...

x is used before it is declared, and so it crashes. (It does not work with an actual store either.)

To Reproduce

https://svelte.dev/repl/e05965bb51ef4ab997af96e53dfc2a8c?version=3.12.1

(see ssr JS output)

Expected behavior

Same behavior as non SSR.

Information about your Svelte project:

  • Svelte version 3.12.1
  • REPL

Severity

Annoyance.

The error mentioned in the title easily worked around by declaring the let variable outside of the reactive block:

let x = {}
$: x = {}

But what's more annoying is that it forces to duplicate the code to compute the value, once for the manual declaration, and once for the reactive block. It can be cumbersome in some cases.

@ghost
Copy link

ghost commented Sep 18, 2019

Ok... but you didn't declare x as a store or even an object with the store API. This wouldn't work, whether SSR or not.

When a store is auto-subscribed, an internal function is called to handle the subscription and a variable of the same name is created in the component context during initialization.

The real question is why a x is not a store with a 'subscribe()' method. isn't thrown by the code.

@rixo
Copy link
Contributor Author

rixo commented Sep 19, 2019

@dkondrad It does not work with an actual store either. The problem is that x is read before it is declared in the generated SSR code. See: https://stackoverflow.com/questions/33198849/what-is-the-temporal-dead-zone

@ghost
Copy link

ghost commented Sep 19, 2019

@rixo, yeah I'm well aware of the temporal dead zone.
My point was you posted a reproduction case that wasn't even a store.

Something I forgot to ask was why are you creating a store with a reactive statement anyway?
The whole reason for using a store is that it takes care of reactivity on its own.

I'm not a maintainer, but I believe this falls squarely in the "it hurts when I do this" (i.e. don't do this) category.
@Conduitry, do you agree?

@rixo
Copy link
Contributor Author

rixo commented Sep 20, 2019

@dkondrad

Well, OK I'm sorry for sending you a tutorial. I took some time in the REPL to pinpoint a minimal repro so I've been a little vexed by your rebuttal "it's on you because it's not a store" to my "(it does not work with an actual store either)", and felt a little tease on your own mistake was fair game. I don't think "let's not fix the bug because the reporter's a jerk" is a constructive response either though.

So, my use case is an <Await {source} /> component that works transparently for different kinds of source (Promise / Observable). The gist of my code is as follow:

<script>
  export let source
  // ...
  // introspect source to pick the right adapter for the source type
  $: wrap = resolveWrapper(source)
  // wrap outputs a store that streams promises, according to the source emisions
  $: wrappedPromise$ = wrap(source)
</script>

{#await $wrappedPromise$}
  ...
{/await}

The store in question is created dynamically based on source. source is reactive because it is a public prop. So it makes sense to use it in a reactive block in order to support hot swapping of the source.

I would think twice if I had to write the boilerplates by hand, but in Svelte you combine two simple (simple for the user, that is) atoms and it becomes a one-liner. Svelte's awesome. ❤️ ❤️ ❤️

@ghost
Copy link

ghost commented Sep 20, 2019

@rixo, fair enough... I accept your tease :)

You should have just led with that example in the first place because that makes a whole lot more sense! I still not sure why a store is needed instead of a plain-ole-promise, but you know more about your problem domain.

Maybe this will be better handled by Rich's code-red branch.

@rixo
Copy link
Contributor Author

rixo commented Sep 27, 2019

Maybe this will be better handled by Rich's code-red branch.

Nope, not yet at least. Same thing apparently. It looks like it's missing that.

@swyxio
Copy link
Contributor

swyxio commented Dec 30, 2019

just linking a possible terser bug for future people googling this problem sveltejs/template#81 - fixed my issue, could fix yours

@Conduitry Conduitry added the bug label Feb 2, 2020
@Conduitry Conduitry self-assigned this Feb 3, 2020
@bencates
Copy link

bencates commented Feb 21, 2020

I've also run into this issue.

Here's a reproduction case with a bit more context. It toggles between two different stores based on the value of a checkbox. This runs fine in the browser, but the SSR build tries to read checkboxStatus before initialization (lines 16 and 17 with generate: 'ssr' and all other flags false).

@arackaf
Copy link
Contributor

arackaf commented Nov 17, 2020

Is this issue still valid? (was it ever?)

$: x = {}

fails for me with

store.subscribe is not a function

with or without SSR, which makes sense. Changing it to an actual store (or presumably an object with a subscribe method) solves it with or without SSR.

@rixo
Copy link
Contributor Author

rixo commented Nov 17, 2020

Yes it's still current: https://svelte.dev/repl/e05965bb51ef4ab997af96e53dfc2a8c?version=3.29.7

It's not SSR code running in the REPL, that's why you don't see the bug. Check the compiled JS output, there's a runtime error in it.

@arackaf
Copy link
Contributor

arackaf commented Nov 18, 2020

If you just change the empty object literal to properly implement the store interface, it works fine.

https://svelte.dev/repl/4133893d920a4458a25e734a826b9509?version=3.29.7

@rixo
Copy link
Contributor Author

rixo commented Nov 18, 2020

No it does not:

image

Please read the whole issue, this discussion has already happened ¯\_(ツ)_/¯

@arackaf
Copy link
Contributor

arackaf commented Nov 18, 2020

Oh yikes - yeah that's definitely a bug :-\

Looks like a fix was started here #5419

but never finished.

@Conduitry
Copy link
Member

This is finally fixed in 3.31.2 as part of a more sweeping change to how store autosubscriptions are handled in SSR code. See the generated code for https://svelte.dev/repl/e05965bb51ef4ab997af96e53dfc2a8c?version=3.31.2

@arackaf
Copy link
Contributor

arackaf commented Jan 5, 2021

Ayyyyy - happy to hear!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

5 participants