Repository: sveltejs/svelte (JavaScript)
Date: 2020-01-22 19:28:30+00:00
Task type: Bug Fix

Issue:
"Cannot read property 'subscribe' of undefined" when store creation is delayed
If you don't assign a store to a variable directly on initialisation you get the error `"Cannot read property 'subscribe' of undefined"` in `svelte@3.0.0-beta.11`.

This is a bit of a contrived example, but demonstrates the problem ([REPL](https://v3.svelte.technology/repl?version=3.0.0-beta.11&gist=ae979c1511a97f1b7138c6b33da4ae86)):

```html
<!-- Foo.svelte -->
<script>
  import { writable } from 'svelte/store'
	
  export let prop = null
  // Assigning a store to this variable further down gives rise to error
  // "Cannot read property 'subscribe' of undefined"
  let label;
  // This works
  // let label = prop === null ? writable('Foo') : writable(prop)

  if (prop === null) {
    label = writable('Foo')
  } else {
    label = writable(prop)
  }
</script> 

<div>{$label}</div>
```

[This worked back in `beta.3`](https://v3.svelte.technology/repl?version=3.0.0-beta.3&gist=ae979c1511a97f1b7138c6b33da4ae86).


Issue discussion:
Simpler example:

```html
<script>
	import { writable } from 'svelte/store';
	let foo;
	foo = writable(42);
</script>

{$foo}
```

I'm not sure what we want to do about this. One of the several things that changed in beta 4 was to subscribe to stores right away after they're declared. This is generally useful but does result in an error in this case. If `foo` is not a store, should the generated `$$subscribe_foo` function check for that and not do anything in that case? Is that worth the bytes? Is it unreasonable to say that if someone refers to `$foo` anywhere in their component, that `foo` is assumed to _always_ be a store?
Hi I'm getting the same problem but only when I run the code in https://codesandbox.io , When I run the code locally no problems. Go figure.
I ran into this issue recently and wasn't able to figure out a way to work around this. Are there any more details on why the creation is delayed (batched for performance?) or when exactly the stores are created?

Edit: in my case, I placed the stores inside a separate `.js` file but I think it's basically the same thing as here.
Everytime I got that error the debugger pointed me to the wrong store, the real one is named on line 6 at `$$subscribe_storeName`
I don't remember what the autosubscription implementation looked like back when this issue was first opened, but the way it is now would actually lend itself pretty easily to supporting this.

```diff
diff --git a/src/runtime/internal/utils.ts b/src/runtime/internal/utils.ts
index c7722e1a0..4ba7e18c1 100644
--- a/src/runtime/internal/utils.ts
+++ b/src/runtime/internal/utils.ts
@@ -43,12 +43,15 @@ export function not_equal(a, b) {
 }
 
 export function validate_store(store, name) {
-	if (!store || typeof store.subscribe !== 'function') {
+	if (store != null && typeof store.subscribe !== 'function') {
 		throw new Error(`'${name}' is not a store with a 'subscribe' method`);
 	}
 }
 
 export function subscribe(store, ...callbacks) {
+	if (store == null) {
+		return noop;
+	}
 	const unsub = store.subscribe(...callbacks);
 	return unsub.unsubscribe ? () => unsub.unsubscribe() : unsub;
 }
```

All subscriptions now go through this one function (to massage the response from RxJS observables), so we can also use this opportunity to make subscribing to a nullish store (and unsubscribing from it later) a no-op if so desired.

Solution patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,7 @@
 
 ## Unreleased
 
+* Make autosubscribing to a nullish store a no-op ([#2181](https://github.com/sveltejs/svelte/issues/2181))
 * Fix updating a `<slot>` inside an `{#if}` or other block ([#4292](https://github.com/sveltejs/svelte/issues/4292))
 * Fix using RxJS observables in `derived` stores ([#4298](https://github.com/sveltejs/svelte/issues/4298))
 * Add dev mode check to disallow duplicate keys in a keyed `{#each}` ([#4301](https://github.com/sveltejs/svelte/issues/4301))
diff --git a/src/runtime/internal/utils.ts b/src/runtime/internal/utils.ts
--- a/src/runtime/internal/utils.ts
+++ b/src/runtime/internal/utils.ts
@@ -43,12 +43,15 @@ export function not_equal(a, b) {
 }
 
 export function validate_store(store, name) {
-	if (!store || typeof store.subscribe !== 'function') {
+	if (store != null && typeof store.subscribe !== 'function') {
 		throw new Error(`'${name}' is not a store with a 'subscribe' method`);
 	}
 }
 
 export function subscribe(store, ...callbacks) {
+	if (store == null) {
+		return noop;
+	}
 	const unsub = store.subscribe(...callbacks);
 	return unsub.unsubscribe ? () => unsub.unsubscribe() : unsub;
 }


Test patch:
diff --git a/test/runtime/samples/store-auto-subscribe-nullish/_config.js b/test/runtime/samples/store-auto-subscribe-nullish/_config.js
new file mode 100644
--- /dev/null
+++ b/test/runtime/samples/store-auto-subscribe-nullish/_config.js
@@ -0,0 +1,13 @@
+import { writable } from '../../../../store';
+
+export default {
+	html: `
+		<p>undefined</p>
+	`,
+	async test({ assert, component, target }) {
+		component.store = writable('foo');
+		assert.htmlEqual(target.innerHTML, `
+			<p>foo</p>
+		`);
+	}
+};
diff --git a/test/runtime/samples/store-auto-subscribe-nullish/main.svelte b/test/runtime/samples/store-auto-subscribe-nullish/main.svelte
new file mode 100644
--- /dev/null
+++ b/test/runtime/samples/store-auto-subscribe-nullish/main.svelte
@@ -0,0 +1,5 @@
+<script>
+	export let store;
+</script>
+
+<p>{$store}</p>