diff --git a/__tests__/actions.spec.ts b/__tests__/actions.spec.ts index 90ea84549c..bd78a826f3 100644 --- a/__tests__/actions.spec.ts +++ b/__tests__/actions.spec.ts @@ -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') + }) }) diff --git a/__tests__/getters.spec.ts b/__tests__/getters.spec.ts index a70d09f062..3919ce24db 100644 --- a/__tests__/getters.spec.ts +++ b/__tests__/getters.spec.ts @@ -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') + }) }) diff --git a/src/store.ts b/src/store.ts index 76be5843d3..52cbca122f 100644 --- a/src/store.ts +++ b/src/store.ts @@ -8,6 +8,7 @@ import { isPlainObject, StoreWithGetters, StoreGetter, + NonNullObject, } from './types' import { useStoreDevtools } from './devtools' @@ -32,6 +33,15 @@ function innerPatch( 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 { const state: Ref = ref(initialState || buildState()) + const _r = getActiveReq() let isListening = true let subscriptions: SubscriptionCallback[] = [] @@ -151,6 +162,7 @@ export function buildStore< const storeWithState: StoreWithState = { 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 = {} + const computedGetters: StoreWithGetters = {} as StoreWithGetters for (const getterName in getters) { - const method = getters[getterName] - // @ts-ignore - computedGetters[getterName] = computed>(() => - getters[getterName](state.value) - ) + computedGetters[getterName] = computed(() => { + setActiveReq(_r) + return getters[getterName](state.value) + }) as StoreWithGetters[typeof getterName] + } + + const wrappedActions: StoreWithActions = {} as StoreWithActions + 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[typeof actionName] } const store: Store = { ...storeWithState, ...computedGetters, - ...((actions as unknown) as StoreWithActions), + ...wrappedActions, } // make state access invisible @@ -188,17 +207,6 @@ export function buildStore< return store } -type NonNullObject = Record - -/** - * 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 diff --git a/src/types.ts b/src/types.ts index c8776ad808..fbd8d1596f 100644 --- a/src/types.ts +++ b/src/types.ts @@ -14,6 +14,8 @@ export function isPlainObject( ) } +export type NonNullObject = Record + export interface StoreGetter { (state: S): T } @@ -46,6 +48,11 @@ export interface StoreWithState { */ 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