While prospecting the use of useState
with the great Immer, I ended up finding the small use-immer package, which unifies both things. However, it doesn’t have proper TypeScript typings.
So I wrote my own hook to perform this task: just like useState
, it returns a value and a setter, but the setter’s argument is a mutable state, automatically wired to produce
, and all that using a generic argument for proper typing:
import {useCallback, useState} from 'react'; import {Draft, produce} from 'immer'; export function useStateImmer<T>(initialState: T | (() => T)): [ T, (updater: (draft: Draft<T>) => void) => void, ] { const [obj, setObj] = useState(initialState); const setObjProduce = useCallback((updater: (draft: Draft<T>) => void): void => { setObj(curState => { return produce(curState, draft => { updater(draft); }); }); }, [setObj]); return [obj, setObjProduce]; }
Note that the returned setter function is identity, since it’s guarded by an useCallback
call.