1
- import { useEffect , useState } from "react"
1
+ import { useCallback , useSyncExternalStore } from "react"
2
2
3
3
import { LocalStorageKeys } from "@app/utils/defaults"
4
4
@@ -11,38 +11,49 @@ export type Storable =
11
11
| Storable [ ]
12
12
| { [ key : string ] : Storable }
13
13
14
- function coerce < T extends Storable > (
15
- value : string ,
16
- { fallback } : { fallback : T }
17
- ) : T {
18
- try {
19
- return JSON . parse ( value ) as T
20
- } catch ( e ) {
21
- console . error ( e )
22
- return fallback
23
- }
14
+ type KeyValue = ( typeof LocalStorageKeys ) [ keyof typeof LocalStorageKeys ]
15
+
16
+ type UpdateEventPayload = {
17
+ key : string
24
18
}
25
19
26
- type KeyValue = ( typeof LocalStorageKeys ) [ keyof typeof LocalStorageKeys ]
20
+ export class StorageUpdateEvent extends CustomEvent < UpdateEventPayload > {
21
+ static type : keyof WindowEventMap = "rsseditor:storage-update" as const
22
+
23
+ constructor ( payload : UpdateEventPayload ) {
24
+ super ( StorageUpdateEvent . type , { detail : payload } )
25
+ }
26
+ }
27
27
28
28
export default function useLocalStorage < T extends Storable > (
29
29
key : KeyValue ,
30
30
initialValue : T
31
- ) : [ T , React . Dispatch < React . SetStateAction < T > > ] {
32
- const [ value , setValue ] = useState ( ( ) => {
33
- if ( ! window . localStorage ) return initialValue
34
-
35
- const currentValue = localStorage . getItem ( key )
36
- return currentValue
37
- ? coerce < T > ( currentValue , { fallback : initialValue } )
38
- : initialValue
39
- } )
40
-
41
- useEffect ( ( ) => {
42
- if ( window . localStorage ) {
31
+ ) : [ T , ( newValue : T ) => void ] {
32
+ const currentStoredItem = useSyncExternalStore (
33
+ ( callback ) => {
34
+ const listener = ( evt : StorageUpdateEvent ) => {
35
+ if ( evt . detail . key === key ) callback ( )
36
+ }
37
+
38
+ window . addEventListener ( "rsseditor:storage-update" , listener )
39
+ return ( ) => {
40
+ window . removeEventListener ( "rsseditor:storage-update" , listener )
41
+ }
42
+ } ,
43
+ ( ) => localStorage . getItem ( key )
44
+ )
45
+
46
+ const setStoredValue = useCallback (
47
+ ( value : T ) => {
43
48
localStorage . setItem ( key , JSON . stringify ( value ) )
44
- }
45
- } , [ key , value ] )
49
+ window . dispatchEvent ( new StorageUpdateEvent ( { key } ) )
50
+ } ,
51
+ [ key ]
52
+ )
53
+
54
+ const value : T = currentStoredItem
55
+ ? JSON . parse ( currentStoredItem )
56
+ : initialValue
46
57
47
- return [ value , setValue ]
58
+ return [ value , setStoredValue ]
48
59
}
0 commit comments