diff --git a/src/ref.ts b/src/ref.ts new file mode 100644 index 0000000..572d082 --- /dev/null +++ b/src/ref.ts @@ -0,0 +1,9 @@ +import { Eq } from 'fp-ts/Eq'; +import { useRef as useRef_ } from 'react'; + +export const useStableRef = (a: A, eq: Eq): A => { + const ref = useRef_(a); + if (!eq.equals(ref.current, a)) + ref.current = a; + return ref.current; +}; diff --git a/src/state.ts b/src/state.ts index 67eee23..bec0329 100644 --- a/src/state.ts +++ b/src/state.ts @@ -2,20 +2,32 @@ import * as E from 'fp-ts/Either'; import * as Eq from 'fp-ts/Eq'; import * as O from 'fp-ts/Option'; import { Dispatch, SetStateAction, useReducer } from 'react'; +import type { Endomorphism } from 'fp-ts/Endomorphism'; -const isSetStateFn = (s: SetStateAction): s is (a: A) => A => typeof s === 'function'; +import Either = E.Either; +import Option = O.Option; -export const useStable = (initState: A, eq: Eq.Eq): [A, Dispatch>] => +type Eq = Eq.Eq; // eslint-disable-line @typescript-eslint/no-redeclare +type StateTuple = [A, Dispatch>]; + +/** @internal */ +const isSetter = (s: SetStateAction): s is Endomorphism => typeof s === 'function'; + +export const useStable = (initState: A, eq: Eq): StateTuple => useReducer( - (s1: A, s2: SetStateAction) => { - const _s2 = isSetStateFn(s2) ? s2(s1) : s2; - return eq.equals(s1, _s2) ? s1 : _s2; + (prev: A, ss: SetStateAction) => { + const next = isSetter(ss) ? ss(prev) : ss; + return eq.equals(prev, next) ? prev : next; }, initState ); -export const useStableO = (initState: O.Option): [O.Option, Dispatch>>] => - useStable(initState, O.getEq(Eq.eqStrict)); +export const useStableO = (initialState: Option, eq: Eq = Eq.eqStrict): StateTuple> => + useStable(initialState, O.getEq(eq)); -export const useStableE = (initState: E.Either): [E.Either, Dispatch>>] => - useStable(initState, E.getEq(Eq.eqStrict, Eq.eqStrict)); +export function useStableE(initialState: Either): StateTuple>; +export function useStableE(initialState: Either, leftEq: Eq, rightEq: Eq): StateTuple>; +export function useStableE(initialState: Either, leftEq?: Eq, rightEq?: Eq): StateTuple> { + if (leftEq && rightEq) return useStable(initialState, E.getEq(leftEq, rightEq)); + else return useStable(initialState, E.getEq(Eq.eqStrict, Eq.eqStrict)); +}