1import * as SecureStore from 'expo-secure-store';
2import * as React from 'react';
3import { Platform } from 'react-native';
4
5type UseStateHook<T> = [[boolean, T | null], (value?: T | null) => void];
6
7function useAsyncState<T>(initialValue?: [boolean, T | null]): UseStateHook<T> {
8  return React.useReducer(
9    (state: [boolean, T | null], action?: T | null) => [
10      false,
11      action === undefined ? null : action,
12    ],
13    initialValue ?? [true, undefined]
14  ) as UseStateHook<T>;
15}
16
17export async function setStorageItemAsync(key: string, value: string | null) {
18  if (Platform.OS === 'web') {
19    if (value == null) {
20      localStorage.removeItem(key);
21    } else {
22      localStorage.setItem(key, value);
23    }
24  } else {
25    if (value == null) {
26      await SecureStore.deleteItemAsync(key);
27    } else {
28      await SecureStore.setItemAsync(key, value);
29    }
30  }
31}
32
33export function useStorageState(key: string): UseStateHook<string> {
34  // Public
35  const [state, setState] = useAsyncState<string>();
36
37  // Get
38  React.useEffect(() => {
39    if (Platform.OS === 'web') {
40      if (typeof localStorage !== 'undefined') {
41        setState(localStorage.getItem(key));
42      }
43    } else {
44      SecureStore.getItemAsync(key).then((value) => {
45        setState(value);
46      });
47    }
48  }, [key]);
49
50  // Set
51  const setValue = React.useCallback(
52    (value: string | null) => {
53      setStorageItemAsync(key, value).then(() => {
54        setState(value);
55      });
56    },
57    [key]
58  );
59
60  return [state, setValue];
61}
62