Skip to content

Commit

Permalink
fix(reactive-signal): prevent duplicate effect execution caused by co…
Browse files Browse the repository at this point in the history
…mputed
  • Loading branch information
dntzhang committed Sep 3, 2024
1 parent c1bdd2e commit 17de6f4
Show file tree
Hide file tree
Showing 4 changed files with 62 additions and 7 deletions.
4 changes: 2 additions & 2 deletions packages/reactive-signal/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion packages/reactive-signal/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "reactive-signal",
"version": "1.0.3",
"version": "1.0.4",
"scripts": {
"start": "vite",
"dev-vite": "vite",
Expand Down
25 changes: 21 additions & 4 deletions packages/reactive-signal/src/reactivity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,18 @@ export function signal<T>(initialValue?: T): SignalValue<T> {
get(_, prop: keyof SignalValue<T>) {
if (prop === 'value') {
if (activeEffect) {
deps.add(activeEffect)
// activeEffect.originalFn 如果存在就不添加到 deps
let added = false
for (const dep of deps) {
// @ts-ignore
if (dep.originalFn === activeEffect.originalFn) {
added = true
break
}
}
if (!added) {
deps.add(activeEffect)
}
activeEffect.deps?.add({ value, deps }) // Add the dependency to the active effect
}
const component = getActiveComponent()
Expand Down Expand Up @@ -104,7 +115,10 @@ export function signal<T>(initialValue?: T): SignalValue<T> {
export function computed<T>(fn: ComputedFn<T>): SignalValue<T> {
const computedSignal = signal<T>()
effect(() => {
computedSignal.value = fn()
const newValue = fn()
if (computedSignal.peek() !== newValue) {
computedSignal.value = newValue
}
})
return computedSignal
}
Expand All @@ -123,11 +137,14 @@ export function effect(fn: EffectFn): () => void {
if (isRunning) return // If the effect function is already running, return directly
isRunning = true // Start running the effect function
activeEffect = effectFn
fn()
// 防止 effect 重复执行
batch(() => {
fn()
})
activeEffect = null
isRunning = false // Finish running the effect function
},
{ deps },
{ deps, originalFn: fn },
)

// Run the effect function for the first time, which will collect dependencies
Expand Down
38 changes: 38 additions & 0 deletions packages/reactive-signal/test/reactivity.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,44 @@ describe('computed', () => {
expect(computedSignal.peek()).toBe(60)
})

it('should exec correct times', () => {

const count = signal(0)
// Create a computed signal
const doubleCount = computed(() => count.value * 2)
let times = 0
// Create an effect
effect(() => {
times++
console.log(` ${count.value} Double Count: ${doubleCount.value}`)
})

// Update the count signal
count.value = 1
expect(times).toBe(2)

})


it('should exec correct times', () => {

const count = signal(0)
// Create a computed signal
const doubleCount = computed(() => count.value * 2)
const doubleDoubleCount = computed(() => doubleCount.value * 2)
let times = 0
// Create an effect
effect(() => {
times++
console.log(` ${count.value} Double Count: ${doubleCount.value}`)
})

// Update the count signal
count.value = 1
expect(times).toBe(2)

})

it('should not dead loop', () => {
const todos = signal([
{ text: 'Learn OMI', completed: true },
Expand Down

0 comments on commit 17de6f4

Please sign in to comment.