1 import type { Dispatch, SetStateAction } from 'react';
2 import { useCallback, useRef, useState } from 'react';
4 const isFunctionGuard = <S>(setStateAction: SetStateAction<S>): setStateAction is (prevState: S) => S =>
5 typeof setStateAction === 'function';
7 type ReadOnlyRefObject<T> = {
12 <S>(initialState: S | (() => S)): [S, Dispatch<SetStateAction<S>>, ReadOnlyRefObject<S>];
13 <S = undefined>(): [S | undefined, Dispatch<SetStateAction<S | undefined>>, ReadOnlyRefObject<S | undefined>];
17 * Allow to read state values in memoized functions via ref
18 * RefValue is readonly, do not update it's value, it wont be reflected in state.
22 * const [state, setState, stateRef] = useStateRef(0);
24 * const memoizedIncrement = useCallback(() => {
25 * // read state value via ref because
26 * // for some reasons state cannot be in dependency array
27 * if(stateRef.current > 12) ...do something
29 * // do not update stateRef.current, it wont be reflected in state
30 * // prefer the following
31 * setState(prevState => prevState + 1);
36 const useStateRef: UseStateRef = <S>(initialState?: S | (() => S)) => {
37 const [state, setState] = useState(initialState);
38 const ref = useRef(state);
40 const dispatch: typeof setState = useCallback((setStateAction: any) => {
41 ref.current = isFunctionGuard(setStateAction) ? setStateAction(ref.current) : setStateAction;
43 setState(ref.current);
46 return [state, dispatch, ref];
49 export default useStateRef;