Repository: sveltejs/svelte (JavaScript)
Date: 2018-01-17 18:16:34+00:00
Task type: Bug Fix

Issue:
can't supply store to nested components server side
Hey, I'm trying to pass a store to a nested component which works well ordinarily, but it fails when server rendering it.

E.g. I have a manage account page like so:
```html
<div class="page">
    <h1>Manage Account</h1>

    <div>
        <h3>Two Factor Auth</h3>
        <TwoFactorAuth />
    </div>
</div>

<style>
    .page {
        width: 400px;
        margin: 50px auto;
    }
</style>

<script>
    import TwoFactorAuth from './_components/TwoFactorAuth';

    export default {
        components: {
            TwoFactorAuth,
        },
    }
</script>
```

And in the TwoFactorAuth component, using a store via [svelte-redux](https://github.com/UnwrittenFun/svelte-redux)
```html
{{#if $hasTwoFactor}}
<div>yay its enabled</div>
{{else}}
<div>oh no its not enabled</div>
{{/if}}

<script>
    import { connect } from '../../_redux';

    export default {
        store: connect(state => ({
            hasTwoFactor: state.user && state.user.hasTwoFactor,
        })),
    }
</script>
```

As mentioned this fails on the server side, but not when navigation to the page on the client side.

I dug into the generated code a bit and I see it generates the template like so:
```js
return `<div class="page" svelte-1782208320>
    <h1>Manage Account</h1>

    <div>
        <h3>Two Factor Auth</h3>
        ${__WEBPACK_IMPORTED_MODULE_0__components_TwoFactorAuth__["a" /* default */]._render(__result, {}, { store: options.store })}
    </div>
</div>`
```

This is bypassing `render` where the store would be initialised ordinarily, and is instead just passing the parent store.

It errors on the following line in the TwoFactorAuth's `_render` method, as the store is undefined
```js
state = Object.assign(options.store._init(["hasTwoFactor"]), state);
```
```
Cannot read property '_init' of undefined
```


Issue discussion:
Sorry I'm not answering your question in full, but just a quick pointer, generally speaking using the underlying internals (anything starting with `_` such as `_init`) is frowned upon. The suggested approach is to use the official API. If you think there's something missing from the API to make the above possible, bring it up here as a suggestion, and we'll take a deeper look into it.
The use of svelte-redux makes zero difference here, it errors just the same using a regular store. I was just being a bit lazy when it came to providing the example. [Here is a gist](https://gist.github.com/UnwrittenFun/74299ff2166eb5f7c8b7c68e58e4bc6d) that shows the same issue using the vanilla store.

As for using internals of the api, if there was any suggestion it would be take exactly what there is now but make it public, allowing for third party stores that don't extend the built in store itself.

Solution patch:
diff --git a/src/generators/server-side-rendering/index.ts b/src/generators/server-side-rendering/index.ts
--- a/src/generators/server-side-rendering/index.ts
+++ b/src/generators/server-side-rendering/index.ts
@@ -114,7 +114,6 @@ export default function ssr(
 			}
 
 			var result = { head: '', addComponent };
-			${templateProperties.store && `options.store = %store();`}
 			var html = ${name}._render(result, state, options);
 
 			var cssCode = Array.from(components).map(c => c.css && c.css.code).filter(Boolean).join('\\n');
@@ -130,6 +129,7 @@ export default function ssr(
 		}
 
 		${name}._render = function(__result, state, options) {
+			${templateProperties.store && `options.store = %store();`}
 			__result.addComponent(${name});
 
 			state = Object.assign(${initialState.join(', ')});


Test patch:
diff --git a/test/runtime/samples/store-nested/Nested.html b/test/runtime/samples/store-nested/Nested.html
new file mode 100644
--- /dev/null
+++ b/test/runtime/samples/store-nested/Nested.html
@@ -0,0 +1,13 @@
+<h1>Hello, {{$name}}!</h1>
+
+<script>
+	import { Store } from '../../../../store.js';
+
+	export default {
+		store () {
+			return new Store({
+				name: 'world'
+			});
+		},
+	};
+</script>
diff --git a/test/runtime/samples/store-nested/_config.js b/test/runtime/samples/store-nested/_config.js
new file mode 100644
--- /dev/null
+++ b/test/runtime/samples/store-nested/_config.js
@@ -0,0 +1,7 @@
+export default {
+	store: true, // TODO remove this in v2
+
+	html: `
+		<h1>Hello, world!</h1>
+	`,
+};
diff --git a/test/runtime/samples/store-nested/main.html b/test/runtime/samples/store-nested/main.html
new file mode 100644
--- /dev/null
+++ b/test/runtime/samples/store-nested/main.html
@@ -0,0 +1,11 @@
+<Nested/>
+
+<script>
+	import Nested from './Nested.html';
+
+	export default {
+		components: {
+			Nested
+		}
+	};
+</script>