react-usehandlestate
Version:
A modified useState hook which exposes the commonly implmented handleChange function which can be used to modify state. handleChange supports modifying properties of objects stored in state including nested objects and their properties.
1 lines • 3.62 kB
Source Map (JSON)
{"version":3,"sources":["../src/useHandleState.ts"],"sourcesContent":["import { useState } from 'react';\n\n// 1. Generate all valid dot-paths of nested object keys\ntype Path<T> = T extends object\n ? {\n [K in keyof T & string]: T[K] extends object ? K | `${K}.${Path<T[K]>}` : K;\n }[keyof T & string]\n : never;\n\n// 2. Get the type of the value at a given dot-path\ntype PathValue<T, P extends string> = P extends `${infer K}.${infer Rest}`\n ? K extends keyof T\n ? PathValue<T[K], Rest>\n : never\n : P extends keyof T\n ? T[P]\n : never;\n\n// 3. Helper to immutably set nested value at path, fully typed\nfunction setValueAtPath<T, P extends Path<T>>(rootObject: T, path: P, value: PathValue<T, P>): T {\n const keys = path.split('.') as (keyof any)[];\n const newObject = { ...rootObject };\n let current: any = newObject;\n\n for (let i = 0; i < keys.length - 1; i++) {\n const key = keys[i];\n current[key] = { ...current[key] };\n current = current[key];\n }\n\n current[keys[keys.length - 1]] = value;\n return newObject;\n}\n\nfunction getValueAtPath<T>(rootObject: T, path: string): any {\n if (path === '') return rootObject;\n return path\n .split('.')\n .reduce((accumulatorObject: any, key) => accumulatorObject?.[key], rootObject);\n}\n\n// 4. The custom hook\nexport function useHandleState<S>(initialState: S | (() => S)) {\n const [value, setValue] = useState<S>(initialState);\n\n function handleChange(newValue: S | ((prev: S) => S)): void;\n function handleChange<P extends Path<S>>(\n path: P,\n newValue: PathValue<S, P> | ((prev: PathValue<S, P>) => PathValue<S, P>)\n ): void;\n\n function handleChange(param1: any, param2?: any) {\n if (param2 === undefined) {\n setValue(param1);\n } else {\n if (typeof param2 !== 'function') {\n console.log('pram2 no function');\n setValue((prev) => setValueAtPath(prev, param1, param2));\n } else {\n console.log('param2 function');\n setValue((prev) => {\n const prevAtPath = getValueAtPath(prev, param1);\n console.log('prevAtPath: ', prevAtPath);\n const nextAtPath = param2(prevAtPath);\n console.log('nextAtPath: ', nextAtPath);\n return setValueAtPath(prev, param1, nextAtPath);\n });\n }\n }\n }\n\n return [value, handleChange] as const;\n}"],"mappings":"yVAAA,OAAS,YAAAA,MAAgB,QAmBzB,SAASC,EAAqCC,EAAeC,EAASC,EAA2B,CAC/F,IAAMC,EAAOF,EAAK,MAAM,GAAG,EACrBG,EAAYC,EAAA,GAAKL,GACnBM,EAAeF,EAEnB,QAASG,EAAI,EAAGA,EAAIJ,EAAK,OAAS,EAAGI,IAAK,CACxC,IAAMC,EAAML,EAAKI,CAAC,EAClBD,EAAQE,CAAG,EAAIH,EAAA,GAAKC,EAAQE,CAAG,GAC/BF,EAAUA,EAAQE,CAAG,CACvB,CAEA,OAAAF,EAAQH,EAAKA,EAAK,OAAS,CAAC,CAAC,EAAID,EAC1BE,CACT,CAEA,SAASK,EAAkBT,EAAeC,EAAmB,CAC3D,OAAIA,IAAS,GAAWD,EACjBC,EACJ,MAAM,GAAG,EACT,OAAO,CAACS,EAAwBF,IAAQE,GAAA,YAAAA,EAAoBF,GAAMR,CAAU,CACjF,CAGO,SAASW,EAAkBC,EAA6B,CAC7D,GAAM,CAACV,EAAOW,CAAQ,EAAIC,EAAYF,CAAY,EAQlD,SAASG,EAAaC,EAAaC,EAAc,CAC3CA,IAAW,OACbJ,EAASG,CAAM,EAEX,OAAOC,GAAW,YACpB,QAAQ,IAAI,mBAAmB,EAC/BJ,EAAUK,GAASnB,EAAemB,EAAMF,EAAQC,CAAM,CAAC,IAEvD,QAAQ,IAAI,iBAAiB,EAC7BJ,EAAUK,GAAS,CACjB,IAAMC,EAAaV,EAAeS,EAAMF,CAAM,EAC9C,QAAQ,IAAI,eAAgBG,CAAU,EACtC,IAAMC,EAAaH,EAAOE,CAAU,EACpC,eAAQ,IAAI,eAAgBC,CAAU,EAC/BrB,EAAemB,EAAMF,EAAQI,CAAU,CAChD,CAAC,EAGP,CAEA,MAAO,CAAClB,EAAOa,CAAY,CAC7B","names":["useState","setValueAtPath","rootObject","path","value","keys","newObject","__spreadValues","current","i","key","getValueAtPath","accumulatorObject","useHandleState","initialState","setValue","useState","handleChange","param1","param2","prev","prevAtPath","nextAtPath"]}