-
Notifications
You must be signed in to change notification settings - Fork 232
/
Copy pathindex.ts
88 lines (72 loc) · 2.58 KB
/
index.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
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
import { CreateRenderer, Renderer, RenderResult, RenderHook, RenderHookOptions } from '../types'
import { ResultContainer } from '../types/internal'
import { asyncUtils } from './asyncUtils'
import { cleanup, addCleanup, removeCleanup } from './cleanup'
function resultContainer<TValue>(): ResultContainer<TValue> {
const results: Array<{ value?: TValue; error?: Error }> = []
const resolvers: Array<() => void> = []
const result: RenderResult<TValue> = {
get all() {
return results.map(({ value, error }) => error ?? value)
},
get current() {
const { value, error } = results[results.length - 1]
if (error) {
throw error
}
return value as TValue
},
get error() {
const { error } = results[results.length - 1]
return error
}
}
const updateResult = (value?: TValue, error?: Error) => {
results.push({ value, error })
resolvers.splice(0, resolvers.length).forEach((resolve) => resolve())
}
return {
result,
addResolver: (resolver: () => void) => {
resolvers.push(resolver)
},
setValue: (value: TValue) => updateResult(value),
setError: (error: Error) => updateResult(undefined, error)
}
}
function createRenderHook<TProps, TResult, TOptions extends {}, TRenderer extends Renderer<TProps>>(
createRenderer: CreateRenderer<TProps, TResult, TOptions, TRenderer>
) {
const renderHook = (
callback: (props: TProps) => TResult,
options: RenderHookOptions<TProps, TOptions> = {} as RenderHookOptions<TProps, TOptions>
): RenderHook<TProps, TResult, TRenderer> => {
const { result, setValue, setError, addResolver } = resultContainer<TResult>()
const renderProps = { callback, setValue, setError }
let hookProps = options.initialProps
const { render, rerender, unmount, act, ...renderUtils } = createRenderer(renderProps, options)
render(hookProps)
const rerenderHook = (newProps = hookProps) => {
hookProps = newProps
rerender(hookProps)
}
const unmountHook = () => {
removeCleanup(unmountHook)
unmount()
}
addCleanup(unmountHook)
return {
result,
rerender: rerenderHook,
unmount: unmountHook,
...asyncUtils(act, addResolver),
...renderUtils
}
}
// If the function name does not get used before it is returned,
// it's name is removed by babel-plugin-minify-dead-code-elimination.
// This dummy usage works around that.
renderHook.name // eslint-disable-line @typescript-eslint/no-unused-expressions
return renderHook
}
export { createRenderHook, cleanup, addCleanup, removeCleanup }