Skip to content

Commit 2cd10ee

Browse files
committed
feat(vanilla): enable per-proxy granularity for unstable_enableOp
1 parent 61c5422 commit 2cd10ee

File tree

2 files changed

+79
-31
lines changed

2 files changed

+79
-31
lines changed

src/vanilla.ts

Lines changed: 34 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -123,38 +123,41 @@ const createHandlerDefault = <T extends object>(
123123
addPropListener: (prop: string | symbol, propValue: unknown) => void,
124124
removePropListener: (prop: string | symbol) => void,
125125
notifyUpdate: (op: Op | undefined) => void,
126-
): ProxyHandler<T> => ({
127-
deleteProperty(target: T, prop: string | symbol) {
128-
const prevValue = Reflect.get(target, prop)
129-
removePropListener(prop)
130-
const deleted = Reflect.deleteProperty(target, prop)
131-
if (deleted) {
132-
notifyUpdate(createOp?.('delete', prop, prevValue))
133-
}
134-
return deleted
135-
},
136-
set(target: T, prop: string | symbol, value: any, receiver: object) {
137-
const hasPrevValue = !isInitializing() && Reflect.has(target, prop)
138-
const prevValue = Reflect.get(target, prop, receiver)
139-
if (
140-
hasPrevValue &&
141-
(objectIs(prevValue, value) ||
142-
(proxyCache.has(value) && objectIs(prevValue, proxyCache.get(value))))
143-
) {
126+
): ProxyHandler<T> => {
127+
const createOpHandle = createOp
128+
return {
129+
deleteProperty(target: T, prop: string | symbol) {
130+
const prevValue = Reflect.get(target, prop)
131+
removePropListener(prop)
132+
const deleted = Reflect.deleteProperty(target, prop)
133+
if (deleted) {
134+
notifyUpdate(createOpHandle?.('delete', prop, prevValue))
135+
}
136+
return deleted
137+
},
138+
set(target: T, prop: string | symbol, value: any, receiver: object) {
139+
const hasPrevValue = !isInitializing() && Reflect.has(target, prop)
140+
const prevValue = Reflect.get(target, prop, receiver)
141+
if (
142+
hasPrevValue &&
143+
(objectIs(prevValue, value) ||
144+
(proxyCache.has(value) && objectIs(prevValue, proxyCache.get(value))))
145+
) {
146+
return true
147+
}
148+
removePropListener(prop)
149+
if (isObject(value)) {
150+
value = getUntracked(value) || value
151+
}
152+
const nextValue =
153+
!proxyStateMap.has(value) && canProxy(value) ? proxy(value) : value
154+
addPropListener(prop, nextValue)
155+
Reflect.set(target, prop, nextValue, receiver)
156+
notifyUpdate(createOpHandle?.('set', prop, value, prevValue))
144157
return true
145-
}
146-
removePropListener(prop)
147-
if (isObject(value)) {
148-
value = getUntracked(value) || value
149-
}
150-
const nextValue =
151-
!proxyStateMap.has(value) && canProxy(value) ? proxy(value) : value
152-
addPropListener(prop, nextValue)
153-
Reflect.set(target, prop, nextValue, receiver)
154-
notifyUpdate(createOp?.('set', prop, value, prevValue))
155-
return true
156-
},
157-
})
158+
},
159+
}
160+
}
158161

159162
const createOpDefault = (
160163
type: 'set' | 'delete',

tests/subscribe.test.tsx

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -266,3 +266,48 @@ describe('subscribe with op', () => {
266266
expect(handler).lastCalledWith([['delete', ['nested', 'count'], 1]])
267267
})
268268
})
269+
270+
describe('subscribe to a specific proxy that has op enabled', () => {
271+
beforeEach(() => {
272+
vi.useFakeTimers()
273+
})
274+
275+
afterEach(() => {
276+
vi.useRealTimers()
277+
})
278+
279+
it('obj1 should notify ops and obj2 not', async () => {
280+
unstable_enableOp(true)
281+
const obj1 = proxy<{ count1: number; count2?: number }>({
282+
count1: 0,
283+
count2: 0,
284+
})
285+
unstable_enableOp(false)
286+
const obj2 = proxy<{ count1: number; count2?: number }>({
287+
count1: 0,
288+
count2: 0,
289+
})
290+
const handler1 = vi.fn()
291+
292+
subscribe(obj1, handler1)
293+
294+
obj1.count1 += 1
295+
obj1.count2 = 2
296+
297+
const handler2 = vi.fn()
298+
299+
subscribe(obj2, handler2)
300+
301+
obj2.count1 += 1
302+
obj2.count2 = 2
303+
304+
await vi.advanceTimersByTimeAsync(0)
305+
expect(handler1).toBeCalledTimes(1)
306+
expect(handler1).lastCalledWith([
307+
['set', ['count1'], 1, 0],
308+
['set', ['count2'], 2, 0],
309+
])
310+
expect(handler2).toBeCalledTimes(1)
311+
expect(handler2).lastCalledWith([])
312+
})
313+
})

0 commit comments

Comments
 (0)