@sendbird/uikit-utils
Version:
A collection of utility functions and constants for building chat UI components with Sendbird UIKit.
115 lines (94 loc) • 3.09 kB
text/typescript
/* eslint-disable @typescript-eslint/no-explicit-any */
import type { DependencyList } from 'react';
import { useCallback, useEffect, useLayoutEffect, useReducer, useRef, useState } from 'react';
type Destructor = () => void;
type AsyncEffectCallback = () => void | Destructor | Promise<void> | Promise<Destructor>;
const idPool: { [key: string]: number } = {};
export const useUniqId = (key: string) => {
return useState(() => {
if (!idPool[key]) idPool[key] = 1;
return idPool[key]++;
})[0];
};
export const useUniqHandlerId = (name: string) => {
const id = useUniqId(name);
return `${name}_${id}`;
};
export const useForceUpdate = () => {
const [, updater] = useState(0);
return useCallback(() => updater((prev) => prev + 1), []);
};
export const useAsyncEffect = (asyncEffect: AsyncEffectCallback, deps?: DependencyList) => {
useEffect(createAsyncEffectCallback(asyncEffect), deps);
};
export const useAsyncLayoutEffect = (asyncEffect: AsyncEffectCallback, deps?: DependencyList) => {
useLayoutEffect(createAsyncEffectCallback(asyncEffect), deps);
};
export const useIIFE = <T>(callback: () => T) => {
return iife(callback);
};
const iife = <T extends (...args: any[]) => any>(callback: T): ReturnType<T> => callback();
const createAsyncEffectCallback = (asyncEffect: AsyncEffectCallback) => () => {
const destructor = iife(asyncEffect);
return () => {
if (!destructor) return;
if (destructor instanceof Promise) {
iife(async () => {
const awaitedDestructor = await destructor;
if (awaitedDestructor) awaitedDestructor();
});
} else {
iife(destructor);
}
};
};
export const useIsMountedRef = () => {
const isMounted = useRef(true);
useEffect(() => {
return () => {
isMounted.current = false;
};
}, []);
return isMounted;
};
export const useIsFirstMount = () => {
const isFirstMount = useRef(true);
if (isFirstMount.current) {
isFirstMount.current = false;
return true;
}
return isFirstMount.current;
};
export const useFreshCallback = <T extends Function>(callback: T): T => {
const ref = useRef<T>(callback);
ref.current = callback;
return useCallback(((...args: any[]) => ref.current(...args)) as unknown as T, []);
};
export const useDebounceEffect = (action: () => void, delay: number, deps: DependencyList = []) => {
const timeoutRef = useRef<NodeJS.Timeout>();
useEffect(() => {
timeoutRef.current = setTimeout(async () => {
try {
await action();
} finally {
timeoutRef.current = undefined;
}
}, delay);
return () => {
if (timeoutRef.current) {
clearTimeout(timeoutRef.current);
timeoutRef.current = undefined;
}
};
}, [delay, ...deps]);
};
export const usePartialState = <S>(initialState: S) => {
return useReducer((prev: S, state: Partial<S>) => ({ ...prev, ...state }), initialState);
};
export const useRefTracker = <T>(target: T) => {
const ref = useRef(target);
useLayoutEffect(() => {
ref.current = target;
});
return ref;
};