Skip to content

Commit d2ec8e4

Browse files
committed
test: finalize lifecycle tests for Vue
1 parent 4dbc3c5 commit d2ec8e4

File tree

2 files changed

+86
-3
lines changed

2 files changed

+86
-3
lines changed

packages/vue-redux/src/types.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import type { UseSelectorOptions } from './compositions/use-selector'
2+
import type { DeepReadonly, Ref, UnwrapRef } from 'vue'
23

34
export type EqualityFn<T> = (a: T, b: T) => boolean
45

@@ -18,11 +19,11 @@ export interface TypedUseSelectorComposition<TState> {
1819
<TSelected>(
1920
selector: (state: TState) => TSelected,
2021
equalityFn?: EqualityFn<NoInfer<TSelected>>,
21-
): TSelected
22+
): Readonly<Ref<DeepReadonly<UnwrapRef<TSelected>>>>
2223
<Selected = unknown>(
2324
selector: (state: TState) => Selected,
2425
options?: UseSelectorOptions<Selected>,
25-
): Selected
26+
): Readonly<Ref<DeepReadonly<UnwrapRef<Selected>>>>
2627
}
2728

2829
export type NoInfer<T> = [T][T extends any ? 0 : never]

packages/vue-redux/tests/use-selector.spec.tsx

Lines changed: 83 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,10 @@ import { defineComponent, h, inject, watchSyncEffect } from 'vue'
33
import { createStore } from 'redux'
44
import { cleanup, render, waitFor } from '@testing-library/vue'
55
import { ContextKey, provideStore as provideMock, useSelector } from '../src'
6+
import type { Ref } from 'vue'
7+
import type { Subscription } from '../src/utils/Subscription'
68
import type { TypedUseSelectorComposition } from '../src'
79
import type { AnyAction, Store } from 'redux'
8-
import { Subscription } from '../src/utils/Subscription'
910

1011
describe('Vue', () => {
1112
describe('compositions', () => {
@@ -139,6 +140,87 @@ describe('Vue', () => {
139140
expect(appSubscription!.getListeners().get().length).toBe(2),
140141
)
141142
})
143+
144+
it('unsubscribes when the component is unmounted', async () => {
145+
let appSubscription: Subscription | null = null
146+
147+
const Child = defineComponent(() => {
148+
const count = useNormalSelector((s) => s.count)
149+
return () => <div>{count.value}</div>
150+
})
151+
152+
const Parent = defineComponent(() => {
153+
const contextVal = inject(ContextKey)
154+
appSubscription = contextVal && contextVal.subscription
155+
const count = useNormalSelector((s) => s.count)
156+
return () => (count.value === 0 ? <Child /> : null)
157+
})
158+
159+
const App = defineComponent(() => {
160+
provideMock({ store: normalStore })
161+
return () => <Parent />
162+
})
163+
164+
render(<App />)
165+
// Parent + 1 child component
166+
expect(appSubscription!.getListeners().get().length).toBe(2)
167+
168+
normalStore.dispatch({ type: '' })
169+
170+
// Parent component only
171+
await waitFor(() =>
172+
expect(appSubscription!.getListeners().get().length).toBe(1),
173+
)
174+
})
175+
176+
it('notices store updates between render and store subscription effect', async () => {
177+
const Child = defineComponent(
178+
(props: { count: Ref<number> }) => {
179+
// console.log('Child rendering')
180+
watchSyncEffect(() => {
181+
// console.log('Child layoutEffect: ', props.count.value)
182+
if (props.count.value === 0) {
183+
// console.log('Dispatching store update')
184+
normalStore.dispatch({ type: '' })
185+
}
186+
})
187+
return () => null
188+
},
189+
{
190+
props: ['count'],
191+
},
192+
)
193+
194+
const Comp = defineComponent(() => {
195+
// console.log('Parent rendering, selecting state')
196+
const count = useNormalSelector((s) => s.count)
197+
198+
watchSyncEffect(() => {
199+
// console.log('Parent layoutEffect: ', count)
200+
renderedItems.push(count.value)
201+
})
202+
203+
return () => (
204+
<div>
205+
{count.value}
206+
<Child count={count} />
207+
</div>
208+
)
209+
})
210+
211+
const App = defineComponent(() => {
212+
provideMock({ store: normalStore })
213+
return () => <Comp />
214+
})
215+
216+
// console.log('Starting initial render')
217+
render(<App />)
218+
219+
// With `useSyncExternalStore`, we get three renders of `<Comp>`:
220+
// 1) Initial render, count is 0
221+
// 2) Render due to dispatch, still sync in the initial render's commit phase
222+
expect(renderedItems).toEqual([0, 1])
223+
})
142224
})
143225
})
144226
})

0 commit comments

Comments
 (0)