diff --git a/packages/pinia/__tests__/multipleRoots.spec.ts b/packages/pinia/__tests__/multipleRoots.spec.ts new file mode 100644 index 0000000000..3efdcfe6ef --- /dev/null +++ b/packages/pinia/__tests__/multipleRoots.spec.ts @@ -0,0 +1,98 @@ +import { createPinia, defineStore, providePinia } from '../src' +import { mount } from '@vue/test-utils' +import { defineComponent } from 'vue' + +describe('Multiple Roots', () => { + function defineMyStore() { + return defineStore({ + id: 'main', + state: () => ({ + n: 0, + }), + }) + } + + it('uses the same root in child components by default', () => { + expect.assertions(2) + const pinia = createPinia() + const useStore = defineMyStore() + + const ChildComponent = defineComponent({ + template: 'no', + setup() { + const store = useStore() + expect(store.n).toBe(1) + }, + }) + + mount( + { + template: '', + components: { ChildComponent }, + setup() { + const store = useStore() + expect(store.n).toBe(0) + store.n++ + }, + }, + { global: { plugins: [pinia] } } + ) + }) + + it('can use a new pinia root for all child components', async () => { + expect.assertions(2) + const pinia = createPinia() + const useStore = defineMyStore() + + const ChildComponent = defineComponent({ + template: 'no', + setup() { + const store = useStore() + expect(store.n).toBe(0) + }, + }) + mount( + { + template: '', + components: { ChildComponent }, + setup() { + providePinia(createPinia()) + const store = useStore() + expect(store.n).toBe(0) + store.n++ + }, + }, + { global: { plugins: [pinia] } } + ) + }) + + it('state is shared between child components', async () => { + expect.assertions(3) + const pinia = createPinia() + const useStore = defineMyStore() + + const ChildComponent = defineComponent({ + template: 'no', + props: { counter: { type: Number, required: true } }, + setup(props: { counter: number }) { + const store = useStore() + expect(store.n).toBe(props.counter) + store.n++ + }, + }) + mount( + { + template: + '', + components: { ChildComponent }, + setup() { + const store = useStore() + expect(store.n).toBe(0) + store.n++ + providePinia(createPinia()) + }, + }, + { global: { plugins: [pinia] } } + ) + }) +}) diff --git a/packages/pinia/src/index.ts b/packages/pinia/src/index.ts index 99fbd400e9..6764c85258 100644 --- a/packages/pinia/src/index.ts +++ b/packages/pinia/src/index.ts @@ -1,7 +1,7 @@ /** * @module pinia */ -export { setActivePinia, getActivePinia } from './rootStore' +export { setActivePinia, getActivePinia, providePinia } from './rootStore' export { createPinia } from './createPinia' export type { Pinia, diff --git a/packages/pinia/src/rootStore.ts b/packages/pinia/src/rootStore.ts index 833566dd18..4bdf81777f 100644 --- a/packages/pinia/src/rootStore.ts +++ b/packages/pinia/src/rootStore.ts @@ -4,6 +4,7 @@ import { getCurrentInstance, inject, InjectionKey, + provide, Ref, } from 'vue-demi' import { @@ -97,6 +98,13 @@ export const piniaSymbol = ( __DEV__ ? Symbol('pinia') : /* istanbul ignore next */ Symbol() ) as InjectionKey +/** + * Define which pinia to use in all child components. + */ +export function providePinia(pinia: Pinia) { + provide(piniaSymbol, pinia) +} + /** * Context argument passed to Pinia plugins. */