Skip to content

Commit

Permalink
fix: vue 2 support for provideLocal and injectLocal (#3464)
Browse files Browse the repository at this point in the history
  • Loading branch information
xiaoxiangmoe authored Oct 7, 2023
1 parent 90d3400 commit cf75702
Show file tree
Hide file tree
Showing 5 changed files with 112 additions and 28 deletions.
71 changes: 47 additions & 24 deletions packages/shared/createInjectionState/index.test.ts
Original file line number Diff line number Diff line change
@@ -1,49 +1,54 @@
import { type InjectionKey, type Ref, defineComponent, h, inject, ref } from 'vue-demi'
import { createInjectionState } from '@vueuse/shared'
import { type InjectionKey, type Ref, defineComponent, h, inject, nextTick, ref } from 'vue-demi'
import { createInjectionState, injectLocal } from '@vueuse/shared'
import { describe, expect, it } from 'vitest'
import { mount } from '../../.test'
import { mount, useSetup } from '../../.test'

describe('createInjectionState', () => {
it('should work 1', () => {
it('should work for simple nested component', async () => {
const [useProvideCountState, useCountState] = createInjectionState((initialValue: number) => {
const count = ref(initialValue)
return count
})

let count: Ref<number> | undefined

const ChildComponent = defineComponent({
setup() {
const count = useCountState()
expect(count?.value).toBe(0)
count = useCountState()

return () => h('div')
},
})

const RootComponent = defineComponent({
setup() {
useProvideCountState(0)
useProvideCountState(114514)

return () => h(ChildComponent)
},
})

mount(RootComponent)
const vm = mount(RootComponent)
await nextTick()

expect(count?.value).toBe(114514)
vm.unmount()
})

it('should work (custom key)', () => {
it('should work for custom key', async () => {
const KEY: InjectionKey<Ref<number>> = Symbol('count-state')

const [useProvideCountState, useCountState] = createInjectionState((initialValue: number) => {
const count = ref(initialValue)
return count
}, { injectionKey: KEY })

let count: Ref<number> | undefined
let count2: Ref<number> | undefined
const ChildComponent = defineComponent({
setup() {
const count = useCountState()
expect(count?.value).toBe(0)
const count2 = inject(KEY)
expect(count2?.value).toBe(0)
count = useCountState()
count2 = inject(KEY)

return () => h('div')
},
Expand All @@ -57,25 +62,43 @@ describe('createInjectionState', () => {
},
})

mount(RootComponent)
const vm = mount(RootComponent)
await nextTick()
expect(count?.value).toBe(0)
expect(count2?.value).toBe(0)
vm.unmount()
})

it('allow call provideLocal and injectLocal in same component', () => {
it('allow call useProvidingState and useInjectedState in same component', async () => {
const [useProvideCountState, useCountState] = createInjectionState((initialValue: number) => {
const count = ref(initialValue)
return count
})
const vm = useSetup(() => {
useProvideCountState(114514)
const count = useCountState()!

const CanProvidingStateAndInjectedStateInSameComponent = defineComponent({
setup() {
useProvideCountState(114514)
const count = useCountState()!
expect(count.value).toBe(114514)

return () => h('div')
},
return { count }
})
await nextTick()
expect(vm.count).toBe(114514)
vm.unmount()
})

mount(CanProvidingStateAndInjectedStateInSameComponent)
it('allow call useProvidingState and injectLocal in same component', async () => {
const KEY: InjectionKey<Ref<number>> | string = Symbol('count-state')
const [useProvideCountState] = createInjectionState((initialValue: number) => {
const count = ref(initialValue)
return count
}, { injectionKey: KEY })
const vm = useSetup(() => {
useProvideCountState(114514)
const count = injectLocal(KEY)!

return { count }
})
await nextTick()
expect(vm.count).toBe(114514)
vm.unmount()
})
})
61 changes: 61 additions & 0 deletions packages/shared/injectLocal/index.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import { type InjectionKey, defineComponent, getCurrentInstance, h, nextTick } from 'vue-demi'
import { injectLocal, provideLocal } from '@vueuse/shared'
import { describe, expect, it } from 'vitest'
import { mount, useSetup } from '../../.test'

describe('provideLocal injectLocal deps', () => {
it('depends on getCurrentInstance().proxy === getCurrentInstance().proxy', async () => {
const vm = useSetup(() => {
const instance1 = getCurrentInstance()
const instance2 = getCurrentInstance()
const instanceProxyStable = instance1?.proxy === instance2?.proxy
return { instance1, instance2, instanceProxyStable }
})
await nextTick()
expect(vm.instance1).not.toBeNull()
expect(vm.instance2).not.toBeNull()
expect(vm.instance1).toBeTypeOf('object')
expect(vm.instance2).toBeTypeOf('object')
expect(vm.instanceProxyStable).toBe(true)
vm.unmount()
})
})

describe('provideLocal injectLocal', () => {
it('should work for nested component', async () => {
const CountKey: InjectionKey<number> | string = Symbol('count')
let count: number | undefined
const ChildComponent = defineComponent({
setup() {
count = injectLocal(CountKey)

return () => h('div')
},
})

const RootComponent = defineComponent({
setup() {
provideLocal(CountKey, 2333)

return () => h(ChildComponent)
},
})
const vm = mount(RootComponent)
await nextTick()

expect(count).toBe(2333)
vm.unmount()
})

it('should work for same component', async () => {
const CountKey: InjectionKey<number> | string = Symbol('count')
const vm = useSetup(() => {
provideLocal(CountKey, 2333)
const count = injectLocal(CountKey)!
return { count }
})
await nextTick()
expect(vm.count).toBe(2333)
vm.unmount()
})
})
4 changes: 2 additions & 2 deletions packages/shared/injectLocal/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@ import { localProvidedStateMap } from '../provideLocal/map'
// @ts-expect-error overloads are not compatible
export const injectLocal: typeof inject = (...args) => {
const key = args[0] as string | symbol
const instance = getCurrentInstance()
const instance = getCurrentInstance()?.proxy
if (instance == null)
throw new Error('injectEnhancedAllowanceOfCallsFromTheSameComponent must be called in setup')
throw new Error('injectLocal must be called in setup')

if (localProvidedStateMap.has(instance) && key in localProvidedStateMap.get(instance)!)
return localProvidedStateMap.get(instance)![key]
Expand Down
2 changes: 1 addition & 1 deletion packages/shared/provideLocal/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { localProvidedStateMap } from './map'
* ```
*/
export const provideLocal: typeof provide = (key, value) => {
const instance = getCurrentInstance()
const instance = getCurrentInstance()?.proxy
if (instance == null)
throw new Error('provideLocal must be called in setup')

Expand Down
2 changes: 1 addition & 1 deletion packages/shared/provideLocal/map.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
import type { getCurrentInstance } from 'vue-demi'

export const localProvidedStateMap = new WeakMap<NonNullable<ReturnType<typeof getCurrentInstance>>, Record<string | symbol, any>>()
export const localProvidedStateMap = new WeakMap<NonNullable<NonNullable<ReturnType<typeof getCurrentInstance>>['proxy']>, Record<string | symbol, any>>()

0 comments on commit cf75702

Please sign in to comment.