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
|
import React, { useCallback, useEffect, useInsertionEffect, useRef, useState } from 'react';
export function useMounted() {
const mountedRef = useRef(false);
const isMounted = useCallback(() => mountedRef.current, []);
useEffect(() => {
mountedRef.current = true;
return () => {
mountedRef.current = false;
};
}, []);
return isMounted;
}
export function useStyledRef<T>(): React.RefObject<T | null> {
return useRef<T>(null);
}
export function useCombinedRefs<T>(...refs: (React.Ref<T> | undefined)[]): React.RefCallback<T> {
return useRefCallback((element: T | null) => refs.forEach((ref) => assignToRef(element, ref)));
}
export function assignToRef<T>(element: T | null, ref?: React.Ref<T>) {
if (typeof ref === 'function') {
ref(element);
} else if (ref && element) {
(ref as React.RefObject<T>).current = element;
}
}
export function useBoolean(initialValue = false) {
const [value, setValue] = useState(initialValue);
const setTrue = useCallback(() => setValue(true), []);
const setFalse = useCallback(() => setValue(false), []);
const toggle = useCallback(() => setValue((value) => !value), []);
return [value, setTrue, setFalse, toggle] as const;
}
// This hook returns a function that can be used to force a rerender of a component, and
// additionally also returns a variable that can be used to trigger effects as a result. This is a
// hack and should be avoided unless there are no better ways.
export function useRerenderer(): [() => void, number] {
const [count, setCount] = useState(0);
const rerender = useCallback(() => setCount((count) => count + 1), []);
return [rerender, count];
}
type Fn<T extends unknown[], R> = (...args: T) => R;
export function useEffectEvent<Args extends unknown[]>(
fn: Fn<Args, void | undefined | Promise<void | undefined>>,
): Fn<Args, void> {
const ref = useRef<Fn<Args, void>>(fn);
useInsertionEffect(() => {
ref.current = fn;
}, [fn]);
return useCallback((...args: Args) => ref.current(...args), []);
}
// Alias for useEffectEvent, but with another name since the effect event is named after a very
// specific usecase.
export const useRefCallback = useEffectEvent;
export function useLastDefinedValue<T>(value: T): T {
const [definedValue, setDefinedValue] = useState(value);
useEffect(() => setDefinedValue((prev) => value ?? prev), [value]);
return value ?? definedValue;
}
|