-
Notifications
You must be signed in to change notification settings - Fork 85
/
Copy pathuse-event-callback.ts
85 lines (74 loc) · 3.09 KB
/
use-event-callback.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
import { useEffect, useState, useCallback } from 'react'
import useConstant from 'use-constant'
import { Observable, BehaviorSubject, Subject } from 'rxjs'
import { RestrictArray, VoidAsNull, Not } from './type'
export type VoidableEventCallback<EventValue> = EventValue extends void ? () => void : (e: EventValue) => void
export type EventCallbackState<EventValue, State, Inputs = void> = [
VoidableEventCallback<EventValue>,
[State extends void ? null : State, BehaviorSubject<State | null>, BehaviorSubject<RestrictArray<Inputs> | null>],
]
export type ReturnedState<EventValue, State, Inputs> = [
EventCallbackState<EventValue, State, Inputs>[0],
EventCallbackState<EventValue, State, Inputs>[1][0],
]
export type EventCallback<EventValue, State, Inputs> = Not<
Inputs extends void ? true : false,
(
eventSource$: Observable<EventValue>,
inputs$: Observable<RestrictArray<Inputs>>,
state$: Observable<State>,
) => Observable<State>,
(eventSource$: Observable<EventValue>, state$: Observable<State>) => Observable<State>
>
export function useEventCallback<EventValue>(
callback: EventCallback<EventValue, void, void>,
): ReturnedState<EventValue, void | null, void>
export function useEventCallback<EventValue, State>(
callback: EventCallback<EventValue, State, void>,
initialState: State,
): ReturnedState<EventValue, State, void>
export function useEventCallback<EventValue, State, Inputs>(
callback: EventCallback<EventValue, State, Inputs>,
initialState: State,
inputs: RestrictArray<Inputs>,
): ReturnedState<EventValue, State, Inputs>
export function useEventCallback<EventValue, State = void, Inputs = void>(
callback: EventCallback<EventValue, State, Inputs>,
initialState?: State,
inputs?: RestrictArray<Inputs>,
): ReturnedState<EventValue, State | null, Inputs> {
const initialValue = (typeof initialState !== 'undefined' ? initialState : null) as VoidAsNull<State>
const [state, setState] = useState(initialValue)
const event$ = useConstant(() => new Subject<EventValue>())
const state$ = useConstant(() => new BehaviorSubject<State | null>(initialValue))
const inputs$ = useConstant(
() => new BehaviorSubject<RestrictArray<Inputs> | null>(typeof inputs === 'undefined' ? null : inputs),
)
function eventCallback(e: EventValue) {
return event$.next(e)
}
const returnedCallback = useCallback(eventCallback, [])
useEffect(() => {
inputs$.next(inputs!)
}, inputs || [])
useEffect(() => {
setState(initialValue)
let value$: Observable<State>
if (!inputs) {
value$ = (callback as EventCallback<EventValue, State, void>)(event$, state$ as Observable<State>)
} else {
value$ = (callback as any)(event$, inputs$ as Observable<Inputs>, state$ as Observable<State>)
}
const subscription = value$.subscribe((value) => {
state$.next(value)
setState(value as VoidAsNull<State>)
})
return () => {
subscription.unsubscribe()
state$.complete()
inputs$.complete()
event$.complete()
}
}, []) // immutable forever
return [returnedCallback as VoidableEventCallback<EventValue>, state]
}