]> git.ipfire.org Git - thirdparty/vuejs/pinia.git/commitdiff
refactor: avoid double hydration in setup stores
authorEduardo San Martin Morote <posva13@gmail.com>
Fri, 3 Sep 2021 14:25:25 +0000 (16:25 +0200)
committerEduardo San Martin Morote <posva13@gmail.com>
Fri, 3 Sep 2021 14:25:25 +0000 (16:25 +0200)
packages/pinia/__tests__/ssr.spec.ts
packages/pinia/src/store.ts

index 96502c16b9529356ba4f4c2249a6b85587d0415d..6bd63cbc71dcc5f8974fa58deb4a5b6cdc0782e5 100644 (file)
@@ -1,8 +1,8 @@
 /**
  * @jest-environment node
  */
-import { createPinia } from '../src'
-import { Component, createSSRApp, inject } from 'vue'
+import { createPinia, defineStore } from '../src'
+import { Component, createSSRApp, inject, ref, computed } from 'vue'
 import { renderToString, ssrInterpolate } from '@vue/server-renderer'
 import { useUserStore } from './pinia/stores/user'
 import { useCartStore } from './pinia/stores/cart'
@@ -109,4 +109,74 @@ describe('SSR', () => {
       ]</div>"
     `)
   })
+
+  describe('Setup Store', () => {
+    const useStore = defineStore('main', () => {
+      const count = ref(0)
+      const name = ref('Eduardo')
+      const double = computed(() => count.value * 2)
+
+      function increment() {
+        count.value++
+      }
+
+      return { name, count, double, increment }
+    })
+
+    const App = {
+      ssrRender(ctx: any, push: any, _parent: any) {
+        push(
+          `<button>${ssrInterpolate(ctx.store.count)};${ssrInterpolate(
+            ctx.store.double
+          )};${ssrInterpolate(ctx.store.name)}</button>`
+        )
+      },
+      setup() {
+        const store = useStore()
+        store.count++
+
+        return { store }
+      },
+    }
+
+    it('works', async () => {
+      const { pinia, app } = createMyApp(App)
+
+      pinia.state.value.main = {
+        count: 2,
+        name: 'Eduardo',
+      }
+
+      expect(await renderToString(app)).toBe('<button>3;6;Eduardo</button>')
+    })
+
+    it('store can be changed before rendering', async () => {
+      const { pinia, app } = createMyApp(App)
+
+      pinia.state.value.main = {
+        count: 2,
+        name: 'Eduardo',
+      }
+
+      const store = useStore(pinia)
+      store.count = 10
+
+      expect(await renderToString(app)).toBe('<button>11;22;Eduardo</button>')
+    })
+
+    it('pinia can be changed before rendering', async () => {
+      const { pinia, app } = createMyApp(App)
+
+      pinia.state.value.main = {
+        count: 0,
+        name: 'Eduardo',
+      }
+
+      // create the store before changing
+      useStore(pinia)
+      pinia.state.value.main.name = 'Ed'
+
+      expect(await renderToString(app)).toBe('<button>1;2;Ed</button>')
+    })
+  })
 })
index d450d88c4f47730912e7a9e216ee038d2fea6154..66cd9eb5e5ee1a430c11893abc2dcfff0b504af5 100644 (file)
@@ -410,7 +410,8 @@ function createSetupStore<
     )
   ) as unknown as Store<Id, S, G, A>
 
-  // store the partial store now so the setup of stores can use each other
+  // store the partial store now so the setup of stores can instantiate each other before they are finished without
+  // creating infinite loops.
   pinia._s.set($id, store)
 
   // TODO: idea create skipSerialize that marks properties as non serializable and they are skipped
@@ -430,7 +431,7 @@ function createSetupStore<
         // createOptionStore directly sets the state in pinia.state.value so we
         // can just skip that
       } else if (!buildState) {
-        // we must hydrate the state
+        // in setup stores we must hydrate the state and sync pinia state tree with the refs the user just created
         if (initialState) {
           if (isRef(prop)) {
             prop.value = initialState[key]
@@ -636,7 +637,8 @@ function createSetupStore<
     }
   })
 
-  if (initialState) {
+  // only apply hydrate to option stores with an initial state in pinia
+  if (initialState && buildState) {
     ;(options.hydrate || innerPatch)(store, initialState)
   }