From da5a854d046613b0b68205a2fe7b0ccc32493aa1 Mon Sep 17 00:00:00 2001 From: Andrew Jarrett Date: Tue, 1 Nov 2022 18:50:25 -0500 Subject: [PATCH 1/2] initial commit --- src/ref.ts | 10 ++++++++++ src/state.ts | 34 +++++++++++++++++++++++++--------- 2 files changed, 35 insertions(+), 9 deletions(-) create mode 100644 src/ref.ts diff --git a/src/ref.ts b/src/ref.ts new file mode 100644 index 0000000..b5dfc73 --- /dev/null +++ b/src/ref.ts @@ -0,0 +1,10 @@ +import * as E from 'fp-ts/Either'; +import { Eq } from 'fp-ts/Eq'; +import * as O from 'fp-ts/Option'; +import { Dispatch, SetStateAction, useReducer, useRef as useRef_ } from 'react'; + +export const useRef = (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..a2bdcb6 100644 --- a/src/state.ts +++ b/src/state.ts @@ -1,21 +1,37 @@ +import { Dispatch, SetStateAction, useReducer } from 'react'; + import * as E from 'fp-ts/Either'; -import * as Eq from 'fp-ts/Eq'; +import { eqStrict } from "fp-ts/Eq" import * as O from 'fp-ts/Option'; -import { Dispatch, SetStateAction, useReducer } from 'react'; + +import type { Eq } from "fp-ts/Eq" +import type { Either } from 'fp-ts/lib/Either' +import type { Option } from 'fp-ts/Option' const isSetStateFn = (s: SetStateAction): s is (a: A) => A => typeof s === 'function'; -export const useStable = (initState: A, eq: Eq.Eq): [A, Dispatch>] => +type StateTuple = [A, Dispatch>]; + +export const useStable = (initState: A, eq: Eq): StateTuple => useReducer( - (s1: A, s2: SetStateAction) => { - const _s2 = isSetStateFn(s2) ? s2(s1) : s2; + (...[s1, s2]: StateTuple) => { + const _s2 = isSetStateFn(s2) ? s2(s1) : s2; return eq.equals(s1, _s2) ? s1 : _s2; }, initState ); -export const useStableO = (initState: O.Option): [O.Option, Dispatch>>] => - useStable(initState, O.getEq(Eq.eqStrict)); +export function useStableO(initState: Option): StateTuple> +export function useStableO(initState: Option, eq: Eq): StateTuple> +export function useStableO(initState: Option, eq?: Eq): StateTuple> { + const eq_ = eq ?? eqStrict + return useStable(initState, O.getEq(eq_)) +} + +export function useStableE(initState: Either): StateTuple> +export function useStableE(initState: Either, leftEq: Eq, rightEq: Eq): StateTuple> +export function useStableE(initState: Either, leftEq?: Eq, rightEq?: Eq): StateTuple> { + if (leftEq && rightEq) return useStable(initState, E.getEq(leftEq, rightEq)) + else return useStable(initState, E.getEq(eqStrict, eqStrict)) +} -export const useStableE = (initState: E.Either): [E.Either, Dispatch>>] => - useStable(initState, E.getEq(Eq.eqStrict, Eq.eqStrict)); From 35aa0baad8e385e39063080677a4b13a5b65d079 Mon Sep 17 00:00:00 2001 From: Andrew Jarrett Date: Fri, 20 Jan 2023 06:35:39 -0600 Subject: [PATCH 2/2] chore: cleanup signatures, rename `useRef` to `useStableRef` --- src/ref.ts | 11 +++++------ src/state.ts | 42 +++++++++++++++++++----------------------- 2 files changed, 24 insertions(+), 29 deletions(-) diff --git a/src/ref.ts b/src/ref.ts index b5dfc73..572d082 100644 --- a/src/ref.ts +++ b/src/ref.ts @@ -1,10 +1,9 @@ -import * as E from 'fp-ts/Either'; import { Eq } from 'fp-ts/Eq'; -import * as O from 'fp-ts/Option'; -import { Dispatch, SetStateAction, useReducer, useRef as useRef_ } from 'react'; +import { useRef as useRef_ } from 'react'; -export const useRef = (a: A, eq: Eq): A => { +export const useStableRef = (a: A, eq: Eq): A => { const ref = useRef_(a); - if (!eq.equals(ref.current, a)) ref.current = a + if (!eq.equals(ref.current, a)) + ref.current = a; return ref.current; -} +}; diff --git a/src/state.ts b/src/state.ts index a2bdcb6..bec0329 100644 --- a/src/state.ts +++ b/src/state.ts @@ -1,37 +1,33 @@ -import { Dispatch, SetStateAction, useReducer } from 'react'; - import * as E from 'fp-ts/Either'; -import { eqStrict } from "fp-ts/Eq" +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'; -import type { Eq } from "fp-ts/Eq" -import type { Either } from 'fp-ts/lib/Either' -import type { Option } from 'fp-ts/Option' - -const isSetStateFn = (s: SetStateAction): s is (a: A) => A => typeof s === 'function'; +import Either = E.Either; +import Option = O.Option; +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, s2]: StateTuple) => { - 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 function useStableO(initState: Option): StateTuple> -export function useStableO(initState: Option, eq: Eq): StateTuple> -export function useStableO(initState: Option, eq?: Eq): StateTuple> { - const eq_ = eq ?? eqStrict - return useStable(initState, O.getEq(eq_)) -} +export const useStableO = (initialState: Option, eq: Eq = Eq.eqStrict): StateTuple> => + useStable(initialState, O.getEq(eq)); -export function useStableE(initState: Either): StateTuple> -export function useStableE(initState: Either, leftEq: Eq, rightEq: Eq): StateTuple> -export function useStableE(initState: Either, leftEq?: Eq, rightEq?: Eq): StateTuple> { - if (leftEq && rightEq) return useStable(initState, E.getEq(leftEq, rightEq)) - else return useStable(initState, E.getEq(eqStrict, 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)); } -