Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(runtime-core, reactivity): onEffectCleanup and baseWatch #82

Merged
merged 23 commits into from
Jan 4, 2024
Merged
Show file tree
Hide file tree
Changes from 22 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
f1fe01e
refactor: externalized COMPAT case
LittleSound Dec 25, 2023
d8682e8
feat: initial code of baseWatch
LittleSound Dec 25, 2023
409b52a
refactor: the watch API with baseWatch
LittleSound Dec 26, 2023
db4463c
fix: export onEffectCleanup
LittleSound Dec 26, 2023
2aef609
fix: some cases for server-renderer
LittleSound Dec 26, 2023
97179ed
chore: merge branch 'minor' of https://github.com/vuejs/core into fea…
LittleSound Dec 26, 2023
d1f001b
fix: lint
LittleSound Dec 27, 2023
4d04f5e
fix: treeshaking error
LittleSound Dec 27, 2023
b57405c
fix: treeshaking error
LittleSound Dec 27, 2023
a8dc8e6
fix: tracked in cleanup
LittleSound Dec 27, 2023
7c5f05a
Merge branch 'minor' of https://github.com/vuejs/core into feat/onEff…
LittleSound Dec 28, 2023
56c87ec
test: baseWatch with onEffectCleanup
LittleSound Dec 28, 2023
d99e9a6
test: onEffectCleanup in runtime-core
LittleSound Dec 28, 2023
e9555ce
test: baseWatch
LittleSound Dec 28, 2023
90fd005
chore: organize exports
LittleSound Dec 28, 2023
a078ad1
chore: rename handleWarn to onWarn
LittleSound Dec 28, 2023
f44ef0b
feat: implement getCurrentEffect
LittleSound Dec 31, 2023
2213634
refactor: simplify unwatch implementation
LittleSound Dec 31, 2023
8dd0c1f
chore: merge remote-tracking branch 'origin/minor' into feat/onEffect…
LittleSound Dec 31, 2023
fda1a2a
Merge remote-tracking branch 'LittleSound/core/feat/onEffectCleanup-a…
LittleSound Dec 31, 2023
9eae6cb
Merge remote-tracking branch 'oVapor/main' into feat/vapor-onEffectCl…
LittleSound Jan 4, 2024
58ceb75
fix: modify the code according to the review
LittleSound Jan 4, 2024
c1b3e7c
chore: update
sxzz Jan 4, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
178 changes: 178 additions & 0 deletions packages/reactivity/__tests__/baseWatch.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
import type { Scheduler, SchedulerJob } from '../src/baseWatch'
import {
BaseWatchErrorCodes,
EffectScope,
type Ref,
baseWatch,
onEffectCleanup,
ref,
} from '../src/index'

const queue: SchedulerJob[] = []

// these codes are a simple scheduler
let isFlushPending = false
const resolvedPromise = /*#__PURE__*/ Promise.resolve() as Promise<any>
const nextTick = (fn?: () => any) =>
fn ? resolvedPromise.then(fn) : resolvedPromise
const scheduler: Scheduler = job => {
queue.push(job)
flushJobs()
}
const flushJobs = () => {
if (isFlushPending) return
isFlushPending = true
resolvedPromise.then(() => {
queue.forEach(job => job())
queue.length = 0
isFlushPending = false
})
}

describe('baseWatch', () => {
test('effect', () => {
let dummy: any
const source = ref(0)
baseWatch(() => {
dummy = source.value
})
expect(dummy).toBe(0)
source.value++
expect(dummy).toBe(1)
})

test('watch', () => {
let dummy: any
const source = ref(0)
baseWatch(source, () => {
dummy = source.value
})
expect(dummy).toBe(undefined)
source.value++
expect(dummy).toBe(1)
})

test('custom error handler', () => {
const onError = vi.fn()

baseWatch(
() => {
throw 'oops in effect'
},
null,
{ onError },
)

const source = ref(0)
const effect = baseWatch(
source,
() => {
onEffectCleanup(() => {
throw 'oops in cleanup'
})
throw 'oops in watch'
},
{ onError },
)

expect(onError.mock.calls.length).toBe(1)
expect(onError.mock.calls[0]).toMatchObject([
'oops in effect',
BaseWatchErrorCodes.WATCH_CALLBACK,
])

source.value++
expect(onError.mock.calls.length).toBe(2)
expect(onError.mock.calls[1]).toMatchObject([
'oops in watch',
BaseWatchErrorCodes.WATCH_CALLBACK,
])

effect!.stop()
source.value++
expect(onError.mock.calls.length).toBe(3)
expect(onError.mock.calls[2]).toMatchObject([
'oops in cleanup',
BaseWatchErrorCodes.WATCH_CLEANUP,
])
})

test('baseWatch with onEffectCleanup', async () => {
let dummy = 0
let source: Ref<number>
const scope = new EffectScope()

scope.run(() => {
source = ref(0)
baseWatch(onCleanup => {
source.value

onCleanup(() => (dummy += 2))
onEffectCleanup(() => (dummy += 3))
onEffectCleanup(() => (dummy += 5))
})
})
expect(dummy).toBe(0)

scope.run(() => {
source.value++
})
expect(dummy).toBe(10)

scope.run(() => {
source.value++
})
expect(dummy).toBe(20)

scope.stop()
expect(dummy).toBe(30)
})

test('nested calls to baseWatch and onEffectCleanup', async () => {
let calls: string[] = []
let source: Ref<number>
let copyist: Ref<number>
const scope = new EffectScope()

scope.run(() => {
source = ref(0)
copyist = ref(0)
// sync by default
baseWatch(
() => {
const current = (copyist.value = source.value)
onEffectCleanup(() => calls.push(`sync ${current}`))
},
null,
{},
)
// with scheduler
baseWatch(
() => {
const current = copyist.value
onEffectCleanup(() => calls.push(`post ${current}`))
},
null,
{ scheduler },
)
})

await nextTick()
expect(calls).toEqual([])

scope.run(() => source.value++)
expect(calls).toEqual(['sync 0'])
await nextTick()
expect(calls).toEqual(['sync 0', 'post 0'])
calls.length = 0

scope.run(() => source.value++)
expect(calls).toEqual(['sync 1'])
await nextTick()
expect(calls).toEqual(['sync 1', 'post 1'])
calls.length = 0

scope.stop()
expect(calls).toEqual(['sync 2', 'post 2'])
})
})
Loading