import { useState , useEffect , useRef , useMemo , useCallback } from "react" ; import { RNSensitiveInfoOptions } from "react-native-sensitive-info" ; import { TMigrationFuncParams } from "./createMigrate" ; import createAsyncStorage from "./createAsyncStorage" ; import createSensitiveStorage from "./createSensitiveStorage" ; import { defaultOptions } from "./defaultOptions" ; import { transformStorageValue } from "./utils" ; export interface PersistStorageValue < Value > { _currentVersion : number ; value : Value ; } export type UsePersistStorageOptions < Value = any > = { debug ? : boolean ; version ? : number ; persist ? : boolean ; migrate ? : ( ( params : TMigrationFuncParams ) => PersistStorageValue < Value > ) | null ; sensitive ? : false | RNSensitiveInfoOptions ; } ; type CallbackFn < S > = ( prev : S ) => S ; export type AsyncSetState < S = any > = ( stateOrCallbackFn : S | CallbackFn < S > ) => Promise < void > ; param key param initialValue param options const usePersistStorage = < Value > ( key , initialValue , { debug = defaultOptions . debug , version = defaultOptions . version , persist = defaultOptions . persist , migrate = defaultOptions . migrate , sensitive = defaultOptions . sensitive } = defaultOptions ) : [ Value , AsyncSetState < Value > , boolean ] => { const currentVersion = useRef < number > ( version || 0 ) ; const [ state , setState ] = useState < Value > ( initialValue ) ; const [ restored , setRestored ] = useState < boolean > ( false ) ; const Storage = useMemo ( ( ) => sensitive ? createSensitiveStorage ( sensitive ) : createAsyncStorage ( ) , [ sensitive ] ) ; const logPrefix = sensitive ? "(sensitive)" : "" ; const setValueToStorage = useCallback ( async ( newValue ) => { const value = transformStorageValue < Value > ( newValue , currentVersion . current ) ; try { const serializedValue = JSON . stringify ( value ) ; await Storage . setItem ( key , serializedValue ) ; if ( debug ) { console . debug ( `${ logPrefix }[PersistStorage]: set ${ key }: ` , value ) ; } } catch ( err ) { console . error ( err ) ; } } , [ Storage ] ) ; useEffect ( ( ) => { if ( persist ) { const restoreStateFromStorage = async ( ) => { try { const storageValue = await Storage . getItem ( key ) ; if ( storageValue ) { let parsedValue = JSON . parse ( storageValue || "null" ) ; if ( parsedValue && parsedValue . _currentVersion === undefined ) { parsedValue = transformStorageValue ( parsedValue , currentVersion . current ) ; } if ( migrate ) { parsedValue = migrate ( { key , state : parsedValue , version : currentVersion . current } ) ; currentVersion . current = parsedValue . _currentVersion ; await setValueToStorage ( parsedValue . value ) ; } setState ( parsedValue . value ) ; if ( debug ) { console . debug ( `${ logPrefix }[PersistStorage]: restore ${ key }: ` , parsedValue ) ; } } else { await setValueToStorage ( state ) ; } } catch ( err ) { console . error ( err ) ; } setRestored ( true ) ; } ; restoreStateFromStorage ( ) ; } else { const removePersistItem = async ( ) => { await Storage . removeItem ( key ) ; setRestored ( true ) ; } ; removePersistItem ( ) ; if ( debug ) { console . debug ( `${ logPrefix }[PersistStorage]: remove ${ key }` ) ; } } } , [ ] ) ; const asyncSetState = async ( stateOrCallbackFn ) => { const newValue = stateOrCallbackFn instanceof Function ? stateOrCallbackFn ( state ) : stateOrCallbackFn ; setState ( newValue ) ; if ( persist ) { await setValueToStorage ( newValue ) ; } } ; return [ state , asyncSetState , restored ] ; } ; export default usePersistStorage ;