]> git.ipfire.org Git - thirdparty/vuejs/pinia.git/commitdiff
refactor: use a WeakMap for stores to support SSR
authorEduardo San Martin Morote <posva13@gmail.com>
Tue, 14 Jan 2020 17:35:29 +0000 (18:35 +0100)
committerEduardo San Martin Morote <posva13@gmail.com>
Mon, 20 Jan 2020 18:21:18 +0000 (19:21 +0100)
__tests__/store.patch.spec.ts
__tests__/store.spec.ts
__tests__/subscriptions.spec.ts
src/index.ts
src/store.ts

index d8e74a6409a85b1a7bd612fb52b27cf8d874cfef..3c181a42c2d7889dec5bca8ad2c6e1e78b06533d 100644 (file)
@@ -1,15 +1,17 @@
-import { createStore } from '../src'
+import { createStore, setActiveReq } from '../src'
 
 describe('store.patch', () => {
-  const useStore = createStore('main', () => ({
-    // TODO: the boolean cas shouldn't be necessary
-    // https://www.typescriptlang.org/play/#code/MYewdgzgLgBCMF4YG8CwAoGWYEMBcMUATgK4CmGAvhhiAHQ6IwBmOANhBehqJLMETI4oZJgAoAlIgB8MMclwFi5GJQk10vaDGBMBQkZI3AGTVhzJA
-    a: true as boolean,
-    nested: {
-      foo: 'foo',
-      a: { b: 'string' },
-    },
-  })).bind(null, true) // force always a fresh instance
+  const useStore = () => {
+    // create a new store
+    setActiveReq({})
+    return createStore('main', () => ({
+      a: true,
+      nested: {
+        foo: 'foo',
+        a: { b: 'string' },
+      },
+    }))()
+  }
 
   it('patches a property without touching the rest', () => {
     const store = useStore()
index 39ac7d29e79c3956fdbd9b3bb8a0a33684b4f266..e584b99039a06732d34a409b9c6cf37ff3a499c6 100644 (file)
@@ -1,15 +1,17 @@
-import { createStore } from '../src'
+import { createStore, setActiveReq } from '../src'
 
 describe('Store', () => {
-  const useStore = createStore('main', () => ({
-    // TODO: the boolean cas shouldn't be necessary
-    // https://www.typescriptlang.org/play/#code/MYewdgzgLgBCMF4YG8CwAoGWYEMBcMUATgK4CmGAvhhiAHQ6IwBmOANhBehqJLMETI4oZJgAoAlIgB8MMclwFi5GJQk10vaDGBMBQkZI3AGTVhzJA
-    a: true as boolean,
-    nested: {
-      foo: 'foo',
-      a: { b: 'string' },
-    },
-  })).bind(null, true) // force always a fresh instance
+  const useStore = () => {
+    // create a new store
+    setActiveReq({})
+    return createStore('main', () => ({
+      a: true,
+      nested: {
+        foo: 'foo',
+        a: { b: 'string' },
+      },
+    }))()
+  }
 
   it('sets the initial state', () => {
     const store = useStore()
index 4dbbb9aa7c2e7eac5f37a8876813927a2adee290..6b867a934c360b72923f0d012fcc4336ef328e5f 100644 (file)
@@ -1,9 +1,13 @@
-import { createStore } from '../src'
+import { createStore, setActiveReq } from '../src'
 
 describe('Subscriptions', () => {
-  const useStore = createStore('main', () => ({
-    name: 'Eduardo',
-  })).bind(null, true)
+  const useStore = () => {
+    // create a new store
+    setActiveReq({})
+    return createStore('main', () => ({
+      name: 'Eduardo',
+    }))()
+  }
 
   let store: ReturnType<typeof useStore>
   beforeEach(() => {
index 7c7dc06710ecbfee260aba0c8b3cc15a40b97a17..4d62a5f8aad9264b1851b2b97edf3cb50b821ae7 100644 (file)
@@ -1,2 +1,2 @@
-export { createStore, CombinedStore } from './store'
+export { createStore, CombinedStore, setActiveReq } from './store'
 export { StateTree, StoreGetter } from './types'
index 49d91cbcb82da73a10647f3de0fe18cffa2407da..349950c6c4abf6777c3629656e86e1bac74bd5cf 100644 (file)
@@ -11,6 +11,8 @@ import {
 } from './types'
 import { useStoreDevtools } from './devtools'
 
+const isClient = typeof window != 'undefined'
+
 function innerPatch<T extends StateTree>(
   target: T,
   patchToApply: DeepPartial<T>
@@ -41,12 +43,13 @@ export type CombinedStore<
   G extends Record<string, StoreGetter<S>>
 > = Store<Id, S> & StoreGetters<S, G>
 
+// TODO: allow buildStore to start with an initial state for hydration
+
 /**
  * Creates a store instance
  * @param id unique identifier of the store, like a name. eg: main, cart, user
  * @param initialState initial state applied to the store, Must be correctly typed to infer typings
  */
-
 export function buildStore<
   Id extends string,
   S extends StateTree,
@@ -142,6 +145,13 @@ export function buildStore<
   return store
 }
 
+/**
+ * setActiveReq must be called to handle SSR at the top of functions like `fetch`, `setup`, `serverPrefetch` and others
+ */
+export let activeReq: object = {}
+export const setActiveReq = (req: object) => (activeReq = req)
+export const getActiveReq = () => activeReq
+
 /**
  * The api needs more work we must be able to use the store easily in any
  * function by calling `useStore` to get the store Instance and we also need to
@@ -169,18 +179,19 @@ export function createStore<
   // TODO: do we really need the boolean version for SSR? Using the request would be better
   // as it allows async code like pending requests to use the correct store version.
   return function useStore(
-    req?: object | boolean,
+    force?: boolean,
     initialStates?: Record<Id, S>
   ): CombinedStore<Id, S, G> {
-    if (!req || typeof req === 'boolean') {
-      if (!store || req) store = buildStore(id, buildState, getters)
+    const req = getActiveReq()
+    let stores = storesMap.get(req)
+    if (!stores) storesMap.set(req, (stores = {}))
+
+    store = stores[id]
+    if (!store) {
+      stores[id] = store = buildStore(id, buildState, getters)
+      // hydrate state
       if (initialStates && initialStates[id]) store.state = initialStates[id]
-      useStoreDevtools(store)
-    } else {
-      let stores = storesMap.get(req)
-      if (!stores) storesMap.set(req, (stores = {}))
-      store = stores[id]
-      if (!store) stores[id] = store = buildStore(id, buildState, getters)
+      if (isClient) useStoreDevtools(store)
     }
 
     return store