Skip to content

Commit d825c2a

Browse files
authored
refactor: prefer onInit over setSelf (#3172)
1 parent 6d7fc32 commit d825c2a

File tree

2 files changed

+85
-83
lines changed

2 files changed

+85
-83
lines changed

src/vanilla/utils/loadable.ts

Lines changed: 40 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -21,51 +21,52 @@ export function loadable<Value>(anAtom: Atom<Value>): Atom<Loadable<Value>> {
2121
PromiseLike<Awaited<Value>>,
2222
Loadable<Value>
2323
>()
24-
const refreshAtom = atom(0)
24+
const refreshAtom = atom([() => {}, 0] as [() => void, number])
25+
refreshAtom.unstable_onInit = (store) => {
26+
store.set(refreshAtom, ([, c]) => [
27+
() => store.set(refreshAtom, ([f, c]) => [f, c + 1]),
28+
c,
29+
])
30+
}
2531

2632
if (import.meta.env?.MODE !== 'production') {
2733
refreshAtom.debugPrivate = true
2834
}
2935

30-
const derivedAtom = atom(
31-
(get, { setSelf }) => {
32-
get(refreshAtom)
33-
let value: Value
34-
try {
35-
value = get(anAtom)
36-
} catch (error) {
37-
return { state: 'hasError', error } as Loadable<Value>
38-
}
39-
if (!isPromiseLike<Value>(value)) {
40-
return { state: 'hasData', data: value } as Loadable<Value>
41-
}
42-
const promise = value
43-
const cached1 = loadableCache.get(promise)
44-
if (cached1) {
45-
return cached1
46-
}
47-
promise.then(
48-
(data) => {
49-
loadableCache.set(promise, { state: 'hasData', data })
50-
setSelf()
51-
},
52-
(error) => {
53-
loadableCache.set(promise, { state: 'hasError', error })
54-
setSelf()
55-
},
56-
)
36+
const derivedAtom = atom((get) => {
37+
const [triggerRefresh] = get(refreshAtom)
38+
let value: Value
39+
try {
40+
value = get(anAtom)
41+
} catch (error) {
42+
return { state: 'hasError', error } as Loadable<Value>
43+
}
44+
if (!isPromiseLike<Value>(value)) {
45+
return { state: 'hasData', data: value } as Loadable<Value>
46+
}
47+
const promise = value
48+
const cached1 = loadableCache.get(promise)
49+
if (cached1) {
50+
return cached1
51+
}
52+
promise.then(
53+
(data) => {
54+
loadableCache.set(promise, { state: 'hasData', data })
55+
triggerRefresh()
56+
},
57+
(error) => {
58+
loadableCache.set(promise, { state: 'hasError', error })
59+
triggerRefresh()
60+
},
61+
)
5762

58-
const cached2 = loadableCache.get(promise)
59-
if (cached2) {
60-
return cached2
61-
}
62-
loadableCache.set(promise, LOADING as Loadable<Value>)
63-
return LOADING as Loadable<Value>
64-
},
65-
(_get, set) => {
66-
set(refreshAtom, (c) => c + 1)
67-
},
68-
)
63+
const cached2 = loadableCache.get(promise)
64+
if (cached2) {
65+
return cached2
66+
}
67+
loadableCache.set(promise, LOADING as Loadable<Value>)
68+
return LOADING as Loadable<Value>
69+
})
6970

7071
if (import.meta.env?.MODE !== 'production') {
7172
derivedAtom.debugPrivate = true

src/vanilla/utils/unwrap.ts

Lines changed: 45 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -47,57 +47,58 @@ export function unwrap<Value, Args extends unknown[], Result, PendingValue>(
4747
PromiseLike<unknown>,
4848
Awaited<Value>
4949
>()
50-
const refreshAtom = atom(0)
50+
const refreshAtom = atom([() => {}, 0] as [() => void, number])
51+
refreshAtom.unstable_onInit = (store) => {
52+
store.set(refreshAtom, ([, c]) => [
53+
() => store.set(refreshAtom, ([f, c]) => [f, c + 1]),
54+
c,
55+
])
56+
}
5157

5258
if (import.meta.env?.MODE !== 'production') {
5359
refreshAtom.debugPrivate = true
5460
}
5561

56-
const promiseAndValueAtom: WritableAtom<PromiseAndValue, [], void> & {
62+
const promiseAndValueAtom: Atom<PromiseAndValue> & {
5763
init?: undefined
58-
} = atom(
59-
(get, { setSelf }) => {
60-
get(refreshAtom)
61-
let prev: PromiseAndValue | undefined
62-
try {
63-
prev = get(promiseAndValueAtom) as PromiseAndValue | undefined
64-
} catch {
65-
// ignore previous errors to avoid getting stuck in error state
66-
}
67-
const promise = get(anAtom)
68-
if (!isPromiseLike(promise)) {
69-
return { v: promise as Awaited<Value> }
70-
}
71-
if (promise !== prev?.p) {
72-
promise.then(
73-
(v) => {
74-
promiseResultCache.set(promise, v as Awaited<Value>)
75-
setSelf()
76-
},
77-
(e) => {
78-
promiseErrorCache.set(promise, e)
79-
setSelf()
80-
},
81-
)
82-
}
83-
if (promiseErrorCache.has(promise)) {
84-
throw promiseErrorCache.get(promise)
85-
}
86-
if (promiseResultCache.has(promise)) {
87-
return {
88-
p: promise,
89-
v: promiseResultCache.get(promise) as Awaited<Value>,
90-
}
64+
} = atom((get) => {
65+
const [triggerRefresh] = get(refreshAtom)
66+
let prev: PromiseAndValue | undefined
67+
try {
68+
prev = get(promiseAndValueAtom) as PromiseAndValue | undefined
69+
} catch {
70+
// ignore previous errors to avoid getting stuck in error state
71+
}
72+
const promise = get(anAtom)
73+
if (!isPromiseLike(promise)) {
74+
return { v: promise as Awaited<Value> }
75+
}
76+
if (promise !== prev?.p) {
77+
promise.then(
78+
(v) => {
79+
promiseResultCache.set(promise, v as Awaited<Value>)
80+
triggerRefresh()
81+
},
82+
(e) => {
83+
promiseErrorCache.set(promise, e)
84+
triggerRefresh()
85+
},
86+
)
87+
}
88+
if (promiseErrorCache.has(promise)) {
89+
throw promiseErrorCache.get(promise)
90+
}
91+
if (promiseResultCache.has(promise)) {
92+
return {
93+
p: promise,
94+
v: promiseResultCache.get(promise) as Awaited<Value>,
9195
}
92-
if (prev && 'v' in prev) {
93-
return { p: promise, f: fallback(prev.v), v: prev.v }
94-
}
95-
return { p: promise, f: fallback() }
96-
},
97-
(_get, set) => {
98-
set(refreshAtom, (c) => c + 1)
99-
},
100-
)
96+
}
97+
if (prev && 'v' in prev) {
98+
return { p: promise, f: fallback(prev.v), v: prev.v }
99+
}
100+
return { p: promise, f: fallback() }
101+
})
101102
// HACK to read PromiseAndValue atom before initialization
102103
promiseAndValueAtom.init = undefined
103104

0 commit comments

Comments
 (0)