Skip to content

Commit d5e955f

Browse files
committed
fix(utils): refactor loadable
1 parent c6d0a80 commit d5e955f

1 file changed

Lines changed: 41 additions & 16 deletions

File tree

src/utils/loadable.ts

Lines changed: 41 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -11,32 +11,57 @@ type Loadable<Value> =
1111
| { state: 'hasError'; error: unknown }
1212
| { state: 'hasData'; data: ResolveType<Value> }
1313

14+
const LOADING: Loadable<unknown> = { state: 'loading' }
15+
1416
export function loadable<Value>(anAtom: Atom<Value>): Atom<Loadable<Value>> {
1517
return memoizeAtom(() => {
16-
// TODO we should revisit this for a better solution than refAtom
17-
const refAtom = atom(() => ({} as { prev?: Loadable<Value> }))
18+
const loadableAtomCache = new WeakMap<
19+
Promise<void>,
20+
Atom<Loadable<Value>>
21+
>()
1822

19-
const derivedAtom = atom((get): Loadable<Value> => {
20-
const ref = get(refAtom)
21-
let curr = ref.prev
23+
const catchAtom = atom((get) => {
24+
let promise: Promise<void>
2225
try {
23-
const value = get(anAtom) as ResolveType<Value>
24-
if (curr?.state !== 'hasData' || !Object.is(curr.data, value)) {
25-
curr = { state: 'hasData', data: value }
26-
}
26+
const data = get(anAtom) as ResolveType<Value>
27+
const loadableAtom = atom({ state: 'hasData', data } as Loadable<Value>)
28+
return loadableAtom
2729
} catch (error) {
2830
if (error instanceof Promise) {
29-
if (curr?.state !== 'loading') {
30-
curr = { state: 'loading' }
31-
}
31+
promise = error
3232
} else {
33-
if (curr?.state !== 'hasError' || !Object.is(curr.error, error)) {
34-
curr = { state: 'hasError', error }
33+
const loadableAtom = atom({
34+
state: 'hasError',
35+
error,
36+
} as Loadable<Value>)
37+
return loadableAtom
38+
}
39+
}
40+
const cached = loadableAtomCache.get(promise)
41+
if (cached) {
42+
return cached
43+
}
44+
const loadableAtom = atom(
45+
LOADING as Loadable<Value>,
46+
async (get, set) => {
47+
try {
48+
const data: Value = await get(anAtom, { unstable_promise: true })
49+
set(loadableAtom, { state: 'hasData', data })
50+
} catch (error) {
51+
set(loadableAtom, { state: 'hasError', error })
3552
}
3653
}
54+
)
55+
loadableAtom.onMount = (init) => {
56+
init()
3757
}
38-
ref.prev = curr
39-
return curr as Loadable<Value>
58+
loadableAtomCache.set(promise, loadableAtom)
59+
return loadableAtom
60+
})
61+
62+
const derivedAtom = atom((get) => {
63+
const loadableAtom = get(catchAtom)
64+
return get(loadableAtom)
4065
})
4166

4267
return derivedAtom

0 commit comments

Comments
 (0)