This repository has been archived by the owner on Dec 31, 2020. It is now read-only.
-
-
Notifications
You must be signed in to change notification settings - Fork 91
/
useObserver.ts
64 lines (53 loc) · 1.64 KB
/
useObserver.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
import { Reaction } from "mobx"
import { useDebugValue, useRef } from "react"
import { printDebugValue } from "./printDebugValue"
import { isUsingStaticRendering } from "./staticRendering"
import { useForceUpdate, useUnmount } from "./utils"
export type ForceUpdateHook = () => () => void
export interface IUseObserverOptions {
useForceUpdate?: ForceUpdateHook
}
const EMPTY_OBJECT = {}
export function useObserver<T>(
fn: () => T,
baseComponentName: string = "observed",
options: IUseObserverOptions = EMPTY_OBJECT
): T {
if (isUsingStaticRendering()) {
return fn()
}
const wantedForceUpdateHook = options.useForceUpdate || useForceUpdate
const forceUpdate = wantedForceUpdateHook()
const reaction = useRef<Reaction | null>(null)
if (!reaction.current) {
reaction.current = new Reaction(`observer(${baseComponentName})`, () => {
forceUpdate()
})
}
const dispose = () => {
if (reaction.current && !reaction.current.isDisposed) {
reaction.current.dispose()
}
}
useDebugValue(reaction, printDebugValue)
useUnmount(() => {
dispose()
})
// render the original component, but have the
// reaction track the observables, so that rendering
// can be invalidated (see above) once a dependency changes
let rendering!: T
let exception
reaction.current.track(() => {
try {
rendering = fn()
} catch (e) {
exception = e
}
})
if (exception) {
dispose()
throw exception // re-throw any exceptions catched during rendering
}
return rendering
}