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