【问题标题】:useAsyncStorage custom hook with useState hook in React Native在 React Native 中使用 useState 钩子的 useAsyncStorage 自定义钩子
【发布时间】:2023-10-24 22:11:01
【问题描述】:

众所周知,我们不能在 React Native 中使用 LocalStorage。我尝试创建一个 useAsyncStorage 自定义钩子,它与 LocalStorage 自定义钩子具有相同的功能,以响应将数据存储在移动设备的本地存储中。

使用AsyncStorage.js

import React, { useEffect, useState } from 'react';
import AsyncStorage from '@react-native-community/async-storage';


export default function useAsyncStorage(key, defaultValue) {
  const [storageValue, updateStorageValue] = useState(defaultValue);

  useEffect(() => {
    getStorageValue();
  }, []);

  async function getStorageValue() {
    let value = defaultValue;
    try {
      value = (JSON.parse(await AsyncStorage.getItem(key))) || defaultValue;
    } catch (e) {
      // handle here
    } finally {
      updateStorage(value);
    }
  }

  async function updateStorage(newValue) {
    try {
      await AsyncStorage.setItem(key, JSON.stringify(newValue));
    } catch (e) {
      // handle here
    } finally {
      getStorageValue();
    }
  }

  return [storageValue, updateStorageValue];
}

在 App.js 中,我导入 useAsyncStorage.js 并尝试使用它将数据存储在移动设备的本地存储中。

import useAsyncStorage from './useAsyncStorage';

初始化它:

const [userLevel, setUserLevel] = useAsyncStorage("userLevel",1)

设置值:

 setUserLevel(prevLevel => {
   return prevLevel + 1
 })

它按预期工作并设置数据,但在应用重新加载后无法从 AsyncStorage 中检索数据。

有人可以帮忙吗?我在 useAsyncStorage 自定义挂钩中做错了吗?为什么数据不存储在 AsyncStorage 中?

【问题讨论】:

    标签: javascript reactjs react-native react-hooks asyncstorage


    【解决方案1】:

    这个useAsyncStorage 实现对我有用。

    function useAsyncStorage(key, initialValue) {
      const [storedValue, setStoredValue] = useState();
    
      async function getStoredItem(key, initialValue) {
        try {
          const item = await AsyncStorage.getItem(key);
          const value = item ? JSON.parse(item) : initialValue;
          setStoredValue(value);
        } catch (error) {
          console.log(error);
        }
      }
    
      useEffect(() => {
        getStoredItem(key, initialValue);
      }, [key, initialValue]);
    
      const setValue = async (value) => {
        try {
          const valueToStore =
            value instanceof Function ? value(storedValue) : value;
          setStoredValue(valueToStore);
          await AsyncStorage.setItem(key, JSON.stringify(valueToStore));
        } catch (error) {
          console.log(error);
        }
      };
    
      return [storedValue, setValue];
    }
    

    演示小吃的链接:

    https://snack.expo.io/@yajana/useasyncstorage

    参考:

    https://usehooks.com/useLocalStorage/

    其他类似的实现:

    https://github.com/react-native-hooks/async-storage/blob/master/src/index.js

    【讨论】:

      【解决方案2】:

      我发现这个对我有用。

      import { useEffect, useState } from 'react';
      import AsyncStorage from '@react-native-community/async-storage';
      
      export default function useAsyncStorage(key, initialValue) {
        const [storedValue, setStoredValue] = useState(initialValue);
        useEffect(() => {
          AsyncStorage.getItem(key)
            .then(value => {
              if (value === null) return initialValue;
              return JSON.parse(value);
            })
            .then(setStoredValue)
        }, [key, initialValue]);
      
        const setValue = value => {
          const valueToStore = value instanceof Function ? value(storedValue) : value;
          setStoredValue(valueToStore);
          AsyncStorage.setItem(key, JSON.stringify(valueToStore));
        }
      
        return [storedValue, setValue];
      }
      

      参考:https://gist.github.com/msukmanowsky/08a3650223dda8b102d2c9fe94ad5c12

      【讨论】:

        【解决方案3】:

        你可以试试我做的这个自定义钩子

        import { useEffect, useState } from 'react'
        import AsyncStorage from '@react-native-async-storage/async-storage'
        
        const useAsyncStorage = (key, initialValue) => {
          const [data, setData] = useState(initialValue)
          const [retrivedFromStorage, setRetrievedFromStorage] = useState(false)
        
          useEffect(() => {
            ;(async () => {
              try {
                const value = await AsyncStorage.getItem(key)
                setData(JSON.parse(value) || initialValue)
                setRetrievedFromStorage(true)
              } catch (error) {
                console.error('useAsyncStorage getItem error:', error)
              }
            })()
          }, [key, initialValue])
        
          const setNewData = async (value) => {
            try {
              await AsyncStorage.setItem(key, JSON.stringify(value))
              setData(value)
            } catch (error) {
              console.error('useAsyncStorage setItem error:', error)
            }
          }
        
          return [data, setNewData, retrivedFromStorage]
        }
        export default useAsyncStorage

        您还可以使用 retrievedFromStorage 来检查数据是否从 AsyncStorage 中成功检索。

        【讨论】:

          【解决方案4】:

          我使用 TypeScript 实现 useAsyncStorage 钩子:

          const useAsyncStorage = (key: string) => {
            const [storedData, setStoredData] = useState("");
          
            const storeData = async (value: string | object) => {
              try {
                const valueToStore =
                  typeof value === "string" ? value : JSON.stringify(value);
                await AsyncStorage.setItem(key, valueToStore);
                setStoredData(valueToStore);
              } catch (e) {
                console.error(e);
              }
            };
          
            useEffect(() => {
              const getData = async () => {
                try {
                  const storageValue = await AsyncStorage.getItem(key);
                  if (storageValue !== null) {
                    setStoredData(storageValue);
                  }
                } catch (e) {
                  console.error(e);
                }
              };
              getData();
            }, [key]);
          
            return { storedData, storeData };
          };
          

          【讨论】: