2.6 KB87 lines
Blame
1/**
2 * Copyright (c) Meta Platforms, Inc. and affiliates.
3 *
4 * This source code is licensed under the MIT license found in the
5 * LICENSE file in the root directory of this source tree.
6 */
7
8import deepEqual from 'fast-deep-equal';
9import {useCallback, useEffect, useMemo, useRef} from 'react';
10import {debounce} from './debounce';
11
12/**
13 * Like useEffect, but throttles calls to the effect callback.
14 * This can help avoid overfiring effects that need to happen during render.
15 *
16 * Note: Do not use this just to bypass effects firing twice
17 * in strict + dev mode. Double-firing is done to help detect bugs.
18 * Throttling is not suitable for subscriptions that must stay in sync
19 * or queries which need to stay in sync as things update.
20 *
21 * This is most useful for best-effort side-effects like logging & analytics
22 * which don't require exact synchronization and don't affect UI state.
23 */
24export function useThrottledEffect<A extends Array<unknown>>(
25 cb: (...args: A) => void,
26 throttleTimeMs: number,
27 deps?: Array<unknown>,
28): void {
29 // eslint-disable-next-line react-hooks/exhaustive-deps
30 const throttled = useCallback(debounce(cb, throttleTimeMs, undefined, true), [
31 throttleTimeMs,
32 ...(deps ?? []),
33 ]);
34 return useEffect((...args: A) => {
35 return throttled(...args);
36 // eslint-disable-next-line react-hooks/exhaustive-deps
37 }, deps);
38}
39
40/**
41 * Like React.useMemo, but with deep equality comparison between previous/next dependencies.
42 */
43export function useDeepMemo<T>(construct: () => T, dependencies: React.DependencyList) {
44 const ref = useRef<React.DependencyList>([]);
45 if (!deepEqual(dependencies, ref.current)) {
46 ref.current = dependencies;
47 }
48 const deepDeps = ref.current;
49
50 // eslint-disable-next-line react-hooks/exhaustive-deps
51 return useMemo(construct, deepDeps);
52}
53
54/**
55 * Returns a react ref that you can pass to an element to autofocus it on mount.
56 */
57export function useAutofocusRef<T extends HTMLElement>(): React.MutableRefObject<T | null> {
58 const ref = useRef<T | null>(null);
59 useEffect(() => {
60 if (ref.current != null) {
61 ref.current.focus();
62 }
63 }, [ref]);
64 return ref;
65}
66
67/**
68 * Returns the last (different) value of a given variable from a previous render.
69 */
70export function usePrevious<T>(value: T, equalityFn?: (a: T, b: T) => boolean): T | undefined {
71 const ref = useRef<{value: T; prev: T | undefined}>({
72 value,
73 prev: undefined,
74 });
75
76 const current = ref.current.value;
77
78 if (equalityFn != null ? !equalityFn(value, current) : value !== current) {
79 ref.current = {
80 value,
81 prev: current,
82 };
83 }
84
85 return ref.current?.prev;
86}
87