From: Eduardo San Martin Morote Date: Tue, 14 Jan 2020 17:35:29 +0000 (+0100) Subject: refactor: use a WeakMap for stores to support SSR X-Git-Tag: 0.0.5~23 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=b9a4c29195c1f61a6b6fd3f40247432b9f05ee41;p=thirdparty%2Fvuejs%2Fpinia.git refactor: use a WeakMap for stores to support SSR --- diff --git a/__tests__/store.patch.spec.ts b/__tests__/store.patch.spec.ts index d8e74a64..3c181a42 100644 --- a/__tests__/store.patch.spec.ts +++ b/__tests__/store.patch.spec.ts @@ -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() diff --git a/__tests__/store.spec.ts b/__tests__/store.spec.ts index 39ac7d29..e584b990 100644 --- a/__tests__/store.spec.ts +++ b/__tests__/store.spec.ts @@ -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() diff --git a/__tests__/subscriptions.spec.ts b/__tests__/subscriptions.spec.ts index 4dbbb9aa..6b867a93 100644 --- a/__tests__/subscriptions.spec.ts +++ b/__tests__/subscriptions.spec.ts @@ -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 beforeEach(() => { diff --git a/src/index.ts b/src/index.ts index 7c7dc067..4d62a5f8 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,2 +1,2 @@ -export { createStore, CombinedStore } from './store' +export { createStore, CombinedStore, setActiveReq } from './store' export { StateTree, StoreGetter } from './types' diff --git a/src/store.ts b/src/store.ts index 49d91cbc..349950c6 100644 --- a/src/store.ts +++ b/src/store.ts @@ -11,6 +11,8 @@ import { } from './types' import { useStoreDevtools } from './devtools' +const isClient = typeof window != 'undefined' + function innerPatch( target: T, patchToApply: DeepPartial @@ -41,12 +43,13 @@ export type CombinedStore< G extends Record> > = Store & StoreGetters +// 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 ): CombinedStore { - 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