1+ import { Dispatch , SetStateAction , useRef , useState } from "react" ;
2+ import { StateValidator } from "../models" ;
3+ import { useMemoizedFunction } from "." ;
4+
5+ /**
6+ * **`useStateValidator`**: custom _useState_ hook that validates state on every update.
7+ * @param {T | () => T } initialState - value or a function.
8+ * @param {StateValidator } validator - function that will be executed to validate state.
9+ * @returns {[T, Dispatch<SetStateAction<T>>, T extends Record<string, unknown> ? {[k in keyof T]:{invalid: boolean, message?: string}} : {invalid: boolean, message?: string}] } invalid
10+ * Array with:
11+ * - first element: __state__ value.
12+ * - second element: __setState__ function to update state.
13+ * - third element: __valid__ validation value/object for state.
14+ */
15+ export const useStateValidator = < T > ( initialState : T | ( ( ) => T ) , validator : StateValidator < T > ) : [ T , Dispatch < SetStateAction < T > > , T extends Record < string , unknown > ? { [ k in keyof T ] :{ invalid : boolean , message ?: string } } : { invalid : boolean , message ?: string } ] => {
16+ const refValidator = useRef < { [ k in keyof T ] : { invalid : boolean , message ?: string } } | { invalid : boolean , message ?: string } > ( ) ;
17+ const [ state , setState ] = useState < { state : T , validation : { [ k in keyof T ] : { invalid : boolean , message ?: string } } | { invalid : boolean , message ?: string } } > ( ( ) => {
18+ let state ;
19+ if ( initialState instanceof Function ) {
20+ state = initialState ( ) ;
21+ } else {
22+ state = initialState ;
23+ }
24+ let validation : { [ k in keyof T ] : { invalid : boolean , message ?: string } } | { invalid : boolean , message ?: string } = { } as { [ k in keyof T ] : { invalid : boolean , message ?: string } } ;
25+ if ( ! Array . isArray ( state ) && ! ( state instanceof Date ) && ! ( state instanceof RegExp ) && typeof state === "object" ) {
26+ const keys = Reflect . ownKeys ( state as Record < string , unknown > ) ;
27+ keys . forEach ( ( key ) => {
28+ Reflect . set ( validation , key , { invalid : false } ) ;
29+ } )
30+ } else {
31+ validation = { invalid : false } ;
32+ }
33+ refValidator . current = validation ;
34+ return {
35+ state : state ! ,
36+ validation : validation
37+ }
38+ } ) ;
39+ const update = useMemoizedFunction ( ( val : T | ( ( value : T ) => T ) ) => {
40+ const newState = val instanceof Function
41+ ? val ( state . state )
42+ : val ;
43+ const validation = validator ( newState , JSON . parse ( JSON . stringify ( refValidator . current ) ) as T extends Record < string , unknown > ? { [ k in keyof T ] : { invalid : boolean , message ?: string } } : { invalid : boolean , message ?: string } ) ;
44+ setState ( { state : newState , validation : validation } ) ;
45+ } ) ;
46+
47+ return [ state . state , update , state . validation as T extends Record < string , unknown > ? { [ k in keyof T ] : { invalid : boolean , message ?: string } } :{ invalid : boolean , message ?: string } ] ;
48+ }
0 commit comments