-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathlocal.tsx
111 lines (101 loc) · 3.48 KB
/
local.tsx
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
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
import React, { Context, ReactNode, useState, createContext, useEffect, useMemo, useRef } from 'react';
import { EventEmitter } from 'events';
import { isNull } from 'lodash';
import Debug from 'debug';
import { IStoreContext, IUseStore, defaultContext, useStore } from './store';
const debug = Debug('store:local');
const localStorageEvent = new EventEmitter();
export const LocalContext = createContext(defaultContext);
const stringify = (item) => {
if (typeof(item) === 'undefined' || isNull(item)) return '';
return JSON.stringify(item);
};
export const LocalStoreProvider = ({
context = LocalContext,
children,
}: {
context?: Context<IStoreContext>;
children?: ReactNode;
}) => {
const [useStore] = useState(() => {
return function useStore<T extends any>(
key: string,
defaultValue: T,
): ReturnType<IUseStore<T>> {
const memoDefaultValue = useMemo(() => defaultValue, []);
const [value, _setValue] = useState<string>(typeof(localStorage) === 'undefined' ? stringify(memoDefaultValue) : (localStorage.hasOwnProperty(key) ? localStorage.getItem(key) : stringify(memoDefaultValue)));
const stateRef = useRef<any>();
stateRef.current = value;
useEffect(
() => {
const hasOwnProperty = localStorage.hasOwnProperty(key);
debug('init', { key, defaultValue: memoDefaultValue, hasOwnProperty });
if (!hasOwnProperty) {
const json = stringify(memoDefaultValue);
localStorage.setItem(key, json);
_setValue(json);
}
const fn = (value) => {
const item = localStorage.getItem(key);
if (typeof(item) === 'undefined' || isNull(item)) _setValue(stringify(memoDefaultValue));
else _setValue(value);
};
localStorageEvent.on(key, fn);
return () => {
localStorageEvent.off(key, fn);
};
},
[],
);
const [setValue] = useState(() => (value) => {
debug('setValue', { key, defaultValue: memoDefaultValue, value });
let current;
try {
current = JSON.parse(stateRef.current);
} catch(error) {
current = undefined;
}
const _value = typeof(value) === 'function' ? value(current) : value;
const json = stringify(_value);
localStorage.setItem(key, json);
_setValue(json);
localStorageEvent.emit(key, json);
});
const [unsetValue] = useState(() => () => {
debug('unsetValue', { key, defaultValue: memoDefaultValue });
localStorage.removeItem(key);
localStorageEvent.emit(key, memoDefaultValue);
});
const _value = useMemo(() => {
try {
return JSON.parse(value);
} catch(error) {
return undefined;
}
}, [value]);
return [_value, setValue, unsetValue, false];
};
});
return <context.Provider value={{ useStore }}>
{children}
</context.Provider>;
};
/**
* A custom React hook to use the local store
*
* @example
* ```
* // Wrap your component with LocalStoreProvider to use useLocalStore hook.
* <LocalStoreProvider>
* <MyComponent />
* </LocalStoreProvider>
*
* function MyComponent() {
* const [value, setValue, unsetValue, isLoading] = useLocalStore('key', 'defaultValue');
* return <div>{value}</div>;
* }
* ```
*/
export function useLocalStore<T extends any>(key: string, defaultValue: T, context = LocalContext) {
return useStore(key, defaultValue, context);
}