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
|
import { useCallback, useEffect, useRef } from "react";
import type {
TAction,
TActionFunc,
TActionHandlerOptions,
TArgOfAction,
TInvocationTrigger,
} from "@/actions";
import { bindAction, unbindAction } from "@/actions";
// eslint-disable-next-line opencut/prefer-object-params -- action subscriptions read best as (action, handler, isActive).
export function useActionHandler<A extends TAction>(
action: A,
handler: TActionFunc<A>,
isActive: TActionHandlerOptions,
) {
const handlerRef = useRef<TActionFunc<A>>(handler);
const isBoundRef = useRef(false);
useEffect(() => {
handlerRef.current = handler;
}, [handler]);
const stableHandler = useCallback(
(...parameters: [TArgOfAction<A>, TInvocationTrigger?]) => {
(
handlerRef.current as (
...handlerParameters: [TArgOfAction<A>, TInvocationTrigger?]
) => void
)(...parameters);
},
[],
) as TActionFunc<A>;
useEffect(() => {
const shouldBind =
isActive === undefined ||
(typeof isActive === "boolean" ? isActive : isActive.current);
if (shouldBind && !isBoundRef.current) {
bindAction(action, stableHandler);
isBoundRef.current = true;
} else if (!shouldBind && isBoundRef.current) {
unbindAction(action, stableHandler);
isBoundRef.current = false;
}
return () => {
unbindAction(action, stableHandler);
isBoundRef.current = false;
};
}, [action, stableHandler, isActive]);
}
|