Skip to content

Commit 40348cf

Browse files
committed
wip: deep computed operator
1 parent c86174f commit 40348cf

File tree

6 files changed

+199
-23
lines changed

6 files changed

+199
-23
lines changed

packages/dotto.x/computed/container.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,9 @@ export const createContainer = (cb, emit, invalidate) => {
1515
emit,
1616
invalidate,
1717
unbind() {
18-
listeners.forEach(sub => run_all(Object.values(sub)))
18+
listeners.forEach(sub => {
19+
run_all([...Object.values(sub), sub[DEEP_HANDLER] || (() => {})])
20+
})
1921
listeners.clear()
2022
destroys.forEach(sub => run_all(Object.values(sub)))
2123
destroys.clear()

packages/dotto.x/computed/deep.js

Lines changed: 6 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -18,25 +18,14 @@ export const deep = (store, query) => {
1818

1919
let listenerBox = listeners.get(store)
2020
if (listenerBox && listenerBox[DEEP_HANDLER]) return store.get(query)
21-
22-
if (listenerBox) {
23-
listenerBox[DEEP_HANDLER] = change(store, emit)
24-
let filtered = Object.entries(listenerBox).filter(
25-
([key]) => key !== DEEP_HANDLER
21+
listeners.set(store, { [DEEP_HANDLER]: change(store, emit) })
22+
if (listenerBox) run_all(Object.values(listenerBox))
23+
if (!destroys.has(store)) {
24+
destroys.set(
25+
store,
26+
onOff(store, () => invalidate(true))
2627
)
27-
// eslint-disable-next-line array-callback-return
28-
filtered.map(([key, cb]) => {
29-
cb()
30-
delete listenerBox[key]
31-
})
32-
} else {
33-
listeners.set(store, { [DEEP_HANDLER]: change(store, emit) })
3428
}
3529

36-
destroys.set(
37-
store,
38-
onOff(store, () => invalidate(true))
39-
)
40-
4130
return store.get(query)
4231
}

packages/dotto.x/computed/deep.test.ts

Lines changed: 179 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
/* eslint-disable @typescript-eslint/restrict-plus-operands */
12
import { createStore } from '../create-store'
23
import { computed } from './computed'
34
import { deep } from './deep'
@@ -100,7 +101,8 @@ it('deep store from computed', () => {
100101

101102
let store = createStore<Deep>({ some: { deep: 1 } })
102103
let someDeep = computed(() => deep(store, 'some.deep'))
103-
let unsub = computed(() => deep(someDeep)).listen(value => {
104+
let nextDeep = computed(() => deep(someDeep))
105+
let unsub = nextDeep.listen(value => {
104106
data.push(value)
105107
})
106108

@@ -111,6 +113,7 @@ it('deep store from computed', () => {
111113

112114
expect(data).toEqual([2, 3, 4, 5])
113115
expect(someDeep.get()).toBe(5)
116+
expect(nextDeep.get()).toBe(5)
114117
unsub()
115118
})
116119

@@ -128,7 +131,6 @@ it('prevent watch', () => {
128131
let someDeep = computed(() => take(store, 'some.deep'))
129132
let unsub = computed(() => {
130133
let value = take(someDeep)
131-
// eslint-disable-next-line @typescript-eslint/restrict-plus-operands
132134
return value * deep(store, 'some.test')
133135
}).listen(value => {
134136
data.push(value)
@@ -143,3 +145,178 @@ it('prevent watch', () => {
143145
expect(someDeep.get()).toBe(5)
144146
unsub()
145147
})
148+
149+
it('prevents diamond dependency problem', () => {
150+
let store = createStore({ count: 0 })
151+
let values: string[] = []
152+
153+
let a = computed(() => {
154+
return 'a' + deep(store, 'count')
155+
})
156+
157+
let b = computed(() => {
158+
return 'b' + deep(store, 'count')
159+
})
160+
161+
let combined = computed(() => {
162+
return take(a) + take(b)
163+
})
164+
165+
let unsubscribe = combined.subscribe(v => {
166+
values.push(v)
167+
})
168+
169+
expect(values).toEqual(['a0b0'])
170+
171+
store.set('count', 1)
172+
expect(values).toEqual(['a0b0', 'a1b1'])
173+
174+
unsubscribe()
175+
})
176+
177+
178+
it('prevent watchss', () => {
179+
type Deep = {
180+
some: {
181+
deep: number
182+
test: number
183+
}
184+
}
185+
186+
let data: (number | undefined)[] = []
187+
188+
let store = createStore<Deep>({ some: { deep: 1, test: 2 } })
189+
let someDeep = computed(() => take(store, 'some.deep'))
190+
let unsub = computed(() => {
191+
let value = deep(someDeep)
192+
return value * deep(store, 'some.test')
193+
}).listen(value => {
194+
data.push(value)
195+
})
196+
197+
store.set('some.deep', 2)
198+
store.set('some.deep', 3)
199+
store.set('some.deep', 4)
200+
store.set('some.deep', 5)
201+
202+
expect(data).toEqual([4, 6, 8, 10])
203+
expect(someDeep.get()).toBe(5)
204+
unsub()
205+
})
206+
207+
it('works without reactive', () => {
208+
expect.assertions(1)
209+
let store = createStore({
210+
some: { deep: { path: 3, test: 2, some: 4 } }
211+
})
212+
213+
let pathMult = computed(() => {
214+
let path = deep(store, 'some.deep.path')
215+
return path * 2
216+
})
217+
218+
let nextDataGetter = computed(() => {
219+
return pathMult.get() / 2
220+
})
221+
222+
let unsub2 = nextDataGetter.subscribe(val => {
223+
expect(val).toBe(3)
224+
})
225+
store.set('some.deep.path', 4)
226+
store.set('some.deep.path', 5)
227+
unsub2()
228+
})
229+
230+
//!!!!
231+
232+
it('re-listen: when listener is last - container will destroy', () => {
233+
let events: number[] = []
234+
let store = createStore({ count: 0 })
235+
236+
let mult = computed(() => {
237+
let count = deep(store, 'count')
238+
return count
239+
})
240+
241+
let unbind = mult.listen(num => {
242+
events.push(num)
243+
})
244+
245+
store.set('count', 1)
246+
store.set('count', 2)
247+
store.set('count', 3)
248+
249+
unbind()
250+
251+
let unbind2 = mult.listen(num => {
252+
events.push(num)
253+
})
254+
255+
store.set('count', 12)
256+
store.set('count', 22)
257+
store.set('count', 32)
258+
259+
unbind2()
260+
expect(events).toEqual([1, 2, 3, 12, 22, 32])
261+
})
262+
263+
it('re-listen: when store is off', () => {
264+
let events: number[] = []
265+
let store = createStore({ count: 0 })
266+
267+
let mult = computed(() => {
268+
let count = deep(store, 'count')
269+
return count
270+
})
271+
272+
mult.listen(num => {
273+
events.push(num)
274+
})
275+
276+
store.set('count', 1)
277+
store.set('count', 2)
278+
store.set('count', 3)
279+
280+
store.off()
281+
let unbind2 = mult.listen(num => {
282+
events.push(num)
283+
})
284+
store.set('count', 12)
285+
store.set('count', 22)
286+
store.set('count', 32)
287+
288+
unbind2()
289+
expect(events).toEqual([1, 2, 3, 12, 22, 32])
290+
})
291+
292+
it.skip('re-listen: when on of many stores are off', () => {
293+
let events: number[] = []
294+
let store = createStore({ count: 0 })
295+
let store2 = createStore({ count: 0 })
296+
297+
let mult = computed(() => {
298+
return deep(store, 'count') + deep(store2, 'count')
299+
})
300+
301+
mult.listen(num => {
302+
events.push(num)
303+
})
304+
305+
store.set('count', 1)
306+
store.set('count', 2)
307+
store.set('count', 3)
308+
309+
store.off()
310+
311+
store2.set('count', 12)
312+
store2.set('count', 22)
313+
store2.set('count', 32)
314+
315+
store2.off()
316+
317+
store2.set('count', 12)
318+
store2.set('count', 22)
319+
store2.set('count', 32)
320+
321+
expect(events).toEqual([1, 2, 3, 15, 25, 35])
322+
})

packages/dotto.x/computed/readable.js

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,9 +53,7 @@ export const readable = (cb, createContainer) => {
5353
get(reactive) {
5454
if (reactive) return cb()
5555
let targetContainer = target()
56-
5756
if (!targetContainer) return cb()
58-
5957
return decorate(cb, () => {
6058
targetContainer.silent = true
6159
return () => (targetContainer.silent = false)

packages/dotto.x/create-atom/lifecycle.test.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,16 @@ describe('atom lifecycle', () => {
2121
run_all([unsubHook, unsubAtom])
2222
})
2323

24+
it('onCreate (do not call)', () => {
25+
let events: string[] = []
26+
let atom = createAtom(2)
27+
let unsubAtom = atom.subscribe(() => {})
28+
let unsubHook = onCreate(atom, () => events.push('ok'))
29+
atom.subscribe(() => {})
30+
expect(events).toEqual([])
31+
run_all([unsubHook, unsubAtom])
32+
})
33+
2434
it('onOff (from listen)', () => {
2535
let events: string[] = []
2636
let atom = createAtom(2)

packages/dotto.x/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@
107107
"name": "Computed",
108108
"path": "./computed/index.js",
109109
"import": "{ computed }",
110-
"limit": "617B"
110+
"limit": "631B"
111111
},
112112
{
113113
"name": "all",

0 commit comments

Comments
 (0)