@@ -8,29 +8,49 @@ import {
8
8
9
9
const identity = ( x : any ) => x
10
10
11
+ // 12 hours, but only used if TTL key explicitly provided
12
+ const DEFAULT_TTL_DURATION = 12 * 60 * 60 * 1000
13
+
14
+ type TTLConfig = {
15
+ key : string
16
+ duration ?: number
17
+ }
18
+
11
19
// useState with localStorage persistence
12
20
function usePersistedState < T > (
13
21
key : string ,
14
22
defaultValue : T ,
23
+ ttl ?: TTLConfig ,
15
24
parser = identity
16
25
) : [ T , Dispatch < SetStateAction < T > > ] {
17
- const getLocalStorageValue = useCallback ( ( ) => {
26
+ const ttlKey = ttl ? `plural-${ ttl . key } -timestamp` : ''
27
+ const itemKey = `plural-${ key } `
28
+ const getInitialVal = useCallback ( ( ) => {
18
29
try {
19
- const item = localStorage . getItem ( `plural-${ key } ` )
20
-
30
+ // if TTL key provided, check if it's expired or not found
31
+ if ( ttl ) {
32
+ const timestamp = JSON . parse ( localStorage . getItem ( ttlKey ) ?? 'null' )
33
+ if (
34
+ ! timestamp ||
35
+ Date . now ( ) - timestamp > ( ttl . duration ?? DEFAULT_TTL_DURATION )
36
+ )
37
+ return defaultValue
38
+ }
39
+ const item = localStorage . getItem ( itemKey )
21
40
if ( item ) return parser ( JSON . parse ( item ) )
22
41
} catch ( error ) {
23
42
console . log ( 'Error on localStorage.getItem of' , key )
24
43
}
25
44
26
45
return defaultValue
27
- } , [ key , defaultValue , parser ] )
46
+ } , [ key , defaultValue , parser , ttl ] )
28
47
29
- const [ state , setState ] = useState < T > ( getLocalStorageValue ( ) )
48
+ const [ state , setState ] = useState < T > ( getInitialVal ( ) )
30
49
31
50
useEffect ( ( ) => {
32
- localStorage . setItem ( `plural-${ key } ` , JSON . stringify ( state ) )
33
- } , [ key , state ] )
51
+ localStorage . setItem ( itemKey , JSON . stringify ( state ) )
52
+ if ( ttlKey !== '' ) localStorage . setItem ( ttlKey , JSON . stringify ( Date . now ( ) ) )
53
+ } , [ key , state , ttlKey ] )
34
54
35
55
return [ state , setState ]
36
56
}
0 commit comments