Merge branch 'INDA-330-pii-update' into 'main'
[ProtonMail-WebClient.git] / packages / hooks / useStateRef.ts
blobbd886af822ce7c2015ac86950ad6ea3341166ff5
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> = {
8     readonly current: T;
9 };
11 type UseStateRef = {
12     <S>(initialState: S | (() => S)): [S, Dispatch<SetStateAction<S>>, ReadOnlyRefObject<S>];
13     <S = undefined>(): [S | undefined, Dispatch<SetStateAction<S | undefined>>, ReadOnlyRefObject<S | undefined>];
16 /**
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.
19  *
20  * Example usage:
21  * ```
22  * const [state, setState, stateRef] = useStateRef(0);
23  *
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
28  *
29  *   // do not update stateRef.current, it wont be reflected in state
30  *   // prefer the following
31  *   setState(prevState => prevState + 1);
32  * }, []);
33  *
34  * ```
35  */
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);
44     }, []);
46     return [state, dispatch, ref];
49 export default useStateRef;