|
1 | | -import { useEffect, useState, SyntheticEvent } from 'react' |
2 | | -import { Observable, Subject, noop } from 'rxjs' |
| 1 | +import { useEffect, useMemo, useState, SyntheticEvent } from 'react' |
| 2 | +import { Observable, BehaviorSubject, Subject, noop } from 'rxjs' |
3 | 3 |
|
4 | | -export type EventCallbackState<T, U> = [((e: SyntheticEvent<T>) => void) | typeof noop, U] |
| 4 | +export type VoidAsNull<T> = T extends void ? null : T |
5 | 5 |
|
6 | | -export type EventCallback<T, U> = (eventSource$: Observable<SyntheticEvent<T>>) => Observable<U> |
| 6 | +export type EventCallbackState<_T, E, U, I = void> = [ |
| 7 | + (e: E) => void, |
| 8 | + [U extends void ? null : U, BehaviorSubject<U | null>, BehaviorSubject<I | null>] |
| 9 | +] |
| 10 | +export type ReturnedState<T, E, U, I> = [EventCallbackState<T, E, U, I>[0], EventCallbackState<T, E, U, I>[1][0]] |
7 | 11 |
|
8 | | -export function useEventCallback<T, U = void>(callback: EventCallback<T, U>): EventCallbackState<T, U | null> |
9 | | -export function useEventCallback<T, U = void>(callback: EventCallback<T, U>, initialState: U): EventCallbackState<T, U> |
| 12 | +export type EventCallback<_T, E, U, I> = I extends void |
| 13 | + ? (eventSource$: Observable<E>, state$: Observable<U>) => Observable<U> |
| 14 | + : (eventSource$: Observable<E>, inputs$: Observable<I>, state$: Observable<U>) => Observable<U> |
10 | 15 |
|
11 | | -export function useEventCallback<T, U = void>( |
12 | | - callback: EventCallback<T, U>, |
| 16 | +export function useEventCallback<T, E extends SyntheticEvent<T>, U = void>( |
| 17 | + callback: EventCallback<T, E, U, void>, |
| 18 | +): ReturnedState<T, E, U | null, void> |
| 19 | +export function useEventCallback<T, E extends SyntheticEvent<T>, U = void>( |
| 20 | + callback: EventCallback<T, E, U, void>, |
| 21 | + initialState: U, |
| 22 | +): ReturnedState<T, E, U, void> |
| 23 | +export function useEventCallback<T, E extends SyntheticEvent<T>, U = void, I = void>( |
| 24 | + callback: EventCallback<T, E, U, I>, |
| 25 | + initialState: U, |
| 26 | + inputs: I, |
| 27 | +): ReturnedState<T, E, U, I> |
| 28 | + |
| 29 | +export function useEventCallback<T, E extends SyntheticEvent<T>, U = void, I = void>( |
| 30 | + callback: EventCallback<T, E, U, I>, |
13 | 31 | initialState?: U, |
14 | | -): EventCallbackState<T, U | null> { |
15 | | - const initialValue = typeof initialState !== 'undefined' ? initialState : null |
16 | | - const [state, setState] = useState<EventCallbackState<T, U | null>>([noop, initialValue]) |
| 32 | + inputs?: I, |
| 33 | +): ReturnedState<T, E, U | null, I> { |
| 34 | + const initialValue = (typeof initialState !== 'undefined' ? initialState : null) as VoidAsNull<U> |
| 35 | + const inputSubject$ = new BehaviorSubject<I | null>(typeof inputs === 'undefined' ? null : inputs) |
| 36 | + const stateSubject$ = new BehaviorSubject<U | null>(initialValue) |
| 37 | + const [state, setState] = useState(initialValue) |
| 38 | + const [returnedCallback, setEventCallback] = useState<(e: E) => void>(noop) |
| 39 | + const [state$] = useState(stateSubject$) |
| 40 | + const [inputs$] = useState(inputSubject$) |
| 41 | + |
| 42 | + useMemo(() => { |
| 43 | + inputs$.next(inputs!) |
| 44 | + }, ((inputs as unknown) as ReadonlyArray<any>) || []) |
| 45 | + |
17 | 46 | useEffect( |
18 | 47 | () => { |
19 | | - const event$ = new Subject<SyntheticEvent<T>>() |
20 | | - function eventCallback(e: SyntheticEvent<T>) { |
| 48 | + const event$ = new Subject<E>() |
| 49 | + function eventCallback(e: E) { |
21 | 50 | return event$.next(e) |
22 | 51 | } |
23 | | - setState([eventCallback, initialValue]) |
24 | | - const value$ = callback(event$) |
| 52 | + setState(initialValue) |
| 53 | + setEventCallback(() => eventCallback) |
| 54 | + let value$: Observable<U> |
| 55 | + |
| 56 | + if (!inputs) { |
| 57 | + value$ = (callback as EventCallback<T, E, U, void>)(event$, state$ as Observable<U>) |
| 58 | + } else { |
| 59 | + value$ = (callback as any)(event$, inputs$ as Observable<any>, state$ as Observable<U>) |
| 60 | + } |
25 | 61 | const subscription = value$.subscribe((value) => { |
26 | | - setState([eventCallback, value]) |
| 62 | + state$.next(value) |
| 63 | + setState(value as VoidAsNull<U>) |
27 | 64 | }) |
28 | | - return () => subscription.unsubscribe() |
| 65 | + return () => { |
| 66 | + subscription.unsubscribe() |
| 67 | + state$.complete() |
| 68 | + inputs$.complete() |
| 69 | + event$.complete() |
| 70 | + } |
29 | 71 | }, |
30 | | - [0], // immutable forever |
| 72 | + [], // immutable forever |
31 | 73 | ) |
32 | 74 |
|
33 | | - return state |
| 75 | + return [returnedCallback, state] |
34 | 76 | } |
0 commit comments