/
index.js
88 lines (73 loc) · 2.08 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
import {
useState, useEffect, useCallback, useRef,
} from 'react';
import createGlobalState from './createGlobalState';
const useEventListener = (eventName, handler) => {
const savedHandler = useRef();
useEffect(() => {
savedHandler.current = handler;
});
useEffect(
() => {
const eventListener = event => savedHandler.current(event);
global.addEventListener(eventName, eventListener);
return () => {
global.removeEventListener(eventName, eventListener);
};
},
[eventName]
);
};
const createStorage = provider => ({
get(key, defaultValue) {
const json = provider.getItem(key);
// eslint-disable-next-line no-nested-ternary
return json === null
? typeof defaultValue === 'function'
? defaultValue()
: defaultValue
: JSON.parse(json);
},
set(key, value) {
provider.setItem(key, JSON.stringify(value));
},
});
const createPersistedState = (
key,
provider = global.localStorage
) => (initialState) => {
const globalState = useRef(null);
const createStorageMemoized = useCallback(() => createStorage(provider), [
provider,
]);
const { get, set } = createStorageMemoized();
const [state, setState] = useState(() => get(key, initialState));
// only called on mount/unmount
// subscribe to `storage` change events
useEventListener('storage', ({ key: k, newValue }) => {
const newState = JSON.parse(newValue);
if (k === key && state !== newState) {
setState(newState);
}
});
// only called on mount/unmount
useEffect(() => {
// register a listener that calls `setState` when another instance emits
globalState.current = createGlobalState(key, setState, state);
return () => {
globalState.current.deregister();
};
}, []);
// Only persist to storage if state changes.
useEffect(
() => {
// persist to localStorage
set(key, state);
// inform all of the other instances in this tab
globalState.current.emit(state);
},
[state]
);
return [state, setState];
};
export default createPersistedState;