]> git.ipfire.org Git - thirdparty/vuejs/pinia.git/commitdiff
feat: allow useStore to be called within a store
authorEduardo San Martin Morote <posva13@gmail.com>
Thu, 16 Jan 2020 17:58:35 +0000 (18:58 +0100)
committerEduardo San Martin Morote <posva13@gmail.com>
Mon, 20 Jan 2020 18:21:18 +0000 (19:21 +0100)
__tests__/actions.spec.ts
__tests__/getters.spec.ts
src/store.ts
src/types.ts

index 90ea84549ca5fc5cead6854c3691e3f6f8314b6c..bd78a826f3443616e1ee10169dc1e95b5ff9baf1 100644 (file)
@@ -30,6 +30,24 @@ describe('Store', () => {
     })()
   }
 
+  const useB = createStore({
+    id: 'B',
+    state: () => ({ b: 'b' }),
+  })
+
+  const useA = createStore({
+    id: 'A',
+    state: () => ({ a: 'a' }),
+    actions: {
+      swap() {
+        const bStore = useB()
+        const b = bStore.state.b
+        bStore.state.b = this.state.a
+        this.state.a = b
+      },
+    },
+  })
+
   it('can use the store as this', () => {
     const store = useStore()
     expect(store.state.a).toBe(true)
@@ -45,4 +63,21 @@ describe('Store', () => {
     expect(store.state.a).toBe(false)
     expect(store.state.nested.foo).toBe('bar')
   })
+
+  it('supports being called between requests', () => {
+    const req1 = {}
+    const req2 = {}
+    setActiveReq(req1)
+    const aStore = useA()
+
+    // simulate a different request
+    setActiveReq(req2)
+    const bStore = useB()
+    bStore.state.b = 'c'
+
+    aStore.swap()
+    expect(aStore.state.a).toBe('b')
+    // a different instance of b store was used
+    expect(bStore.state.b).toBe('c')
+  })
 })
index a70d09f06290d5d9339722374192c4eaf1da8a65..3919ce24db4652b9d4c5ce2299bef1db18b4051e 100644 (file)
@@ -15,6 +15,22 @@ describe('Store', () => {
     })()
   }
 
+  const useB = createStore({
+    id: 'B',
+    state: () => ({ b: 'b' }),
+  })
+
+  const useA = createStore({
+    id: 'A',
+    state: () => ({ a: 'a' }),
+    getters: {
+      fromB(state) {
+        const bStore = useB()
+        return state.a + ' ' + bStore.state.b
+      },
+    },
+  })
+
   it('adds getters to the store', () => {
     const store = useStore()
     expect(store.upperCaseName.value).toBe('EDUARDO')
@@ -27,4 +43,19 @@ describe('Store', () => {
     store.state.name = 'Ed'
     expect(store.upperCaseName.value).toBe('ED')
   })
+
+  it('supports changing between requests', () => {
+    const req1 = {}
+    const req2 = {}
+    setActiveReq(req1)
+    const aStore = useA()
+
+    // simulate a different request
+    setActiveReq(req2)
+    const bStore = useB()
+    bStore.state.b = 'c'
+
+    aStore.state.a = 'b'
+    expect(aStore.fromB.value).toBe('b b')
+  })
 })
index 76be5843d319db6ba46e24eb474fd14adbd9bc8c..52cbca122fab64c910b4de60f676b316330575c9 100644 (file)
@@ -8,6 +8,7 @@ import {
   isPlainObject,
   StoreWithGetters,
   StoreGetter,
+  NonNullObject,
 } from './types'
 import { useStoreDevtools } from './devtools'
 
@@ -32,6 +33,15 @@ function innerPatch<T extends StateTree>(
   return target
 }
 
+/**
+ * setActiveReq must be called to handle SSR at the top of functions like `fetch`, `setup`, `serverPrefetch` and others
+ */
+export let activeReq: NonNullObject = {}
+export const setActiveReq = (req: NonNullObject | undefined) =>
+  req && (activeReq = req)
+
+export const getActiveReq = () => activeReq
+
 export interface StoreAction {
   (...args: any[]): any
 }
@@ -103,6 +113,7 @@ export function buildStore<
   initialState?: S | undefined
 ): Store<Id, S, G, A> {
   const state: Ref<S> = ref(initialState || buildState())
+  const _r = getActiveReq()
 
   let isListening = true
   let subscriptions: SubscriptionCallback<S>[] = []
@@ -151,6 +162,7 @@ export function buildStore<
 
   const storeWithState: StoreWithState<Id, S> = {
     id,
+    _r,
     // it is replaced below by a getter
     state: state.value,
 
@@ -159,20 +171,27 @@ export function buildStore<
     reset,
   }
 
-  // @ts-ignore we have to build it
-  const computedGetters: StoreWithGetters<S, G> = {}
+  const computedGetters: StoreWithGetters<S, G> = {} as StoreWithGetters<S, G>
   for (const getterName in getters) {
-    const method = getters[getterName]
-    // @ts-ignore
-    computedGetters[getterName] = computed<ReturnType<typeof method>>(() =>
-      getters[getterName](state.value)
-    )
+    computedGetters[getterName] = computed(() => {
+      setActiveReq(_r)
+      return getters[getterName](state.value)
+    }) as StoreWithGetters<S, G>[typeof getterName]
+  }
+
+  const wrappedActions: StoreWithActions<A> = {} as StoreWithActions<A>
+  for (const actionName in actions) {
+    wrappedActions[actionName] = function() {
+      setActiveReq(_r)
+      // eslint-disable-next-line
+      return actions[actionName].apply(this, arguments as unknown as any[])
+    } as StoreWithActions<A>[typeof actionName]
   }
 
   const store: Store<Id, S, G, A> = {
     ...storeWithState,
     ...computedGetters,
-    ...((actions as unknown) as StoreWithActions<A>),
+    ...wrappedActions,
   }
 
   // make state access invisible
@@ -188,17 +207,6 @@ export function buildStore<
   return store
 }
 
-type NonNullObject = Record<any, any>
-
-/**
- * setActiveReq must be called to handle SSR at the top of functions like `fetch`, `setup`, `serverPrefetch` and others
- */
-export let activeReq: NonNullObject = {}
-export const setActiveReq = (req: NonNullObject | undefined) =>
-  req && (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
index c8776ad80856f3dee9fe453fa566b103b5776278..fbd8d1596f423dde9e43330a8f079b5a4ba14944 100644 (file)
@@ -14,6 +14,8 @@ export function isPlainObject(
   )
 }
 
+export type NonNullObject = Record<any, any>
+
 export interface StoreGetter<S extends StateTree, T = any> {
   (state: S): T
 }
@@ -46,6 +48,11 @@ export interface StoreWithState<Id extends string, S extends StateTree> {
    */
   state: S
 
+  /**
+   * Private property defining the _req for this store
+   */
+  _r: NonNullObject
+
   /**
    * Applies a state patch to current state. Allows passing nested values
    * @param partialState patch to apply to the state