Skip to content

Commit b422fd2

Browse files
authored
Merge pull request #7 from AmbireTech/feature/use-storage
Feature / Use Storage Hook
2 parents 826da00 + 92a41c9 commit b422fd2

2 files changed

Lines changed: 86 additions & 0 deletions

File tree

src/hooks/useStorage/index.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import useStorage from './useStorage'
2+
3+
export default useStorage

src/hooks/useStorage/useStorage.ts

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
import { useCallback, useState } from 'react'
2+
3+
interface Storage {
4+
getItem(key: string): string | null
5+
setItem(key: string, value: string): void
6+
removeItem(key: string): void
7+
}
8+
9+
type HookProps = {
10+
storage: Storage
11+
key: string
12+
defaultValue?: any
13+
isStringStorage?: boolean
14+
setInit?: (item: any) => any
15+
}
16+
17+
const setInitDefault = (item: any): any => item
18+
19+
/**
20+
* The main role of this hook is to hide the Storage provider we are using under the hood (AsyncStorage/localStorage).
21+
* This will allow us to share code between mobile/web.
22+
*
23+
* @param storage - Object implementing Storage interface. For instance, localStorage.
24+
* @param key - Storage item key name.
25+
* @param defaultValue - Default value to be used, in the case the Storage item is not set. If we don't pass it - it will default to null.
26+
* @param isStringStorage - Flag for disabling parsing and item stringifying. If it's enabled, we will treat whatever is in the storage as a string.
27+
* @param setInit - In some advanced cases, we need to perform additional logic for setting the defaultValue, based on the Storage item parsed value.
28+
* setInit function will provide us quick access to the parsed Storage item and based on its value we can return the needed default/init value of the hook.
29+
*/
30+
export default function useStorage({
31+
storage,
32+
key,
33+
defaultValue = null,
34+
isStringStorage = false,
35+
setInit = setInitDefault
36+
}: HookProps): [any, (item: any) => void, () => void] {
37+
const [item, set] = useState(() => {
38+
// In case the item is not set in the storage, we just fall back to `defaultValue`
39+
if (!storage.getItem(key)) return setInit(defaultValue)
40+
41+
if (isStringStorage) return setInit(storage.getItem(key))
42+
43+
// Here we are going to keep the parsed item value.
44+
// If the parsing failed, we just fall back to `defaultValue`.
45+
let parsedItem
46+
47+
try {
48+
parsedItem = JSON.parse(storage.getItem(key)!)
49+
} catch (e) {
50+
console.error(`Storage item parsing failure. Item key: ${key}`, e)
51+
52+
parsedItem = defaultValue
53+
}
54+
55+
return setInit(parsedItem)
56+
})
57+
58+
const setItem = useCallback(
59+
(value: any): void => {
60+
set((prevState: any) => {
61+
const itemValue = typeof value === 'function' ? value(prevState) : value
62+
63+
if (isStringStorage && typeof itemValue !== 'string') {
64+
throw new Error(
65+
`Wrong item type. We expect a string to be passed, but got ${typeof itemValue}!`
66+
)
67+
}
68+
69+
storage.setItem(key, isStringStorage ? itemValue : JSON.stringify(itemValue))
70+
71+
return itemValue
72+
})
73+
},
74+
[storage, key, isStringStorage]
75+
)
76+
77+
const removeItem = useCallback((): void => {
78+
storage.removeItem(key)
79+
set(null)
80+
}, [storage, key])
81+
82+
return [item, setItem, removeItem]
83+
}

0 commit comments

Comments
 (0)