【问题标题】:Setting state along with AsyncStorage in useEffect hook causes infinite loop?在 useEffect 挂钩中设置状态以及 AsyncStorage 会导致无限循环?
【发布时间】:2020-06-04 08:59:22
【问题描述】:

我是 hooks 的新手,最近开始在我的 React Native 项目中使用钩子。

我正在使用AsyncStorage 构建一个简单的待办事项应用程序。首先,我使用 useState 钩子初始化初始 datasetData 状态:

const [data, setData] = useState([]);

我使用两个textInput 和提交按钮将数据保存到 AsyncStorage。这是saveData 函数:

const saveData = async () => {
  const arrData = [{ name: 'vikrant', phone: 123456 }]; // [{ name, phone}] from the textInput

  const storedData = await AsyncStorage.getItem('user');
  const storedDataParsed = JSON.parse(storedData);

  let newData = [];

  if (storedData === null) {
    // save
    await AsyncStorage.setItem('user', JSON.stringify(arrData));
  } else {
    newData = [...storedDataParsed, user];
    await AsyncStorage.setItem('user', JSON.stringify(newData));
  }
  setName('');
  setPhone('');
  Keyboard.dismiss();
};

现在,我使用 useEffect 从 AsyncStorage 获取数据并将其设置为 data 状态。我正在使用数据来呈现屏幕中的文本。

useEffect(() => {
  retrieveData();
}, [data]);

const retrieveData = async () => {
  try {
    const valueString = await AsyncStorage.getItem('user');
    const value = JSON.parse(valueString);
    setData(value);
  } catch (error) {
    console.log(error);
  }
};

我在 useEffect 中使用[data],因为我想在每次数据更改时重新渲染我的组件,即每次我在 AsyncStorage 中保存数据时。但这会导致无限循环,因为setData 会导致 useEffect 无限运行。

如果我从[] 中删除data,它不会循环,但我在渲染中的数据落后了一步。因此,每当我保存数据时,它不会显示当前数据,而是显示前一个数据。

任何解释我在这里做错了什么以及如何解决这个问题?

谢谢。

【问题讨论】:

    标签: reactjs react-native react-hooks asyncstorage


    【解决方案1】:

    正如您已经提到的,无限循环是由于您将 data 作为依赖项传递给 useEffect 并在 useEffect 中调用的函数中设置。

    这里的解决方案是在 AsyncStorage 中设置值时不要使用 useEffect 而是使用 setData

    const saveData = async () => {
      const arrData = [{ name: 'vikrant', phone: 123456 }]; // [{ name, phone}] from the textInput
    
      const storedData = await AsyncStorage.getItem('user');
      const storedDataParsed = JSON.parse(storedData);
    
      let newData = [];
    
      if (storedData === null) {
        // save
        await AsyncStorage.setItem('user', JSON.stringify(arrData));
      } else {
        newData = [...storedDataParsed, user];
        await AsyncStorage.setItem('user', JSON.stringify(newData));
      }
      setName('');
      setPhone('');
      setData(newData);
      Keyboard.dismiss();
    };
    

    【讨论】:

    • 好的,那么就不需要 useEffect 了吗?让我试着按照你的方式去做。
    【解决方案2】:

    只需添加一个条件标志 retrieve 来包装异步存储 retrieveData() 调用。

    同样在“保存数据”的上下文中,我可能只是将异步存储逻辑与状态逻辑分开。当前saveData 被状态和异步存储逻辑污染。

    类似:

    const [retrieve, setRetrieve] = useState(false);
    
    // Pure AsyncStorage context
    const saveData = async () => {
      ...
      if (storedData === null) {
        await AsyncStorage.setItem('user', JSON.stringify(arrData));
      } else {
        newData = [...storedDataParsed, user];
        await AsyncStorage.setItem('user', JSON.stringify(newData));
      }
      // XXX: Removed state logic, call it somewhere else.
    };
    
    const someHandler = async () => {
      await saveData();
      setRetrieve(true); // to signal effect to call retrieveData()
    }
    

    那么效果的目标就是在保存完成后运行retrieveData()

    const [data, setData] = useState([]);
    
    useEffect(() => {
      const retrieveData = async () => {
        try {
          const valueString = await AsyncStorage.getItem('user');
          const value = JSON.parse(valueString);
          // Other set states
          setData(value);
        } catch (error) {
          console.log(error);
        }
      };
      // Retrieve if has new data
      if (retrieve)
        retrieveData();
        setRetrieve(false);
      }
    }, [retrieve]);
    

    【讨论】:

    • 让我试试这个。谢谢。
    • 修复了循环问题,但保存时数据未呈现。
    • @vikrantnegi007 已更新。我已经检查过 data 不需要在 useEffect 依赖项中。效果的目标只是从异步存储中检索或不检索。
    • 我删除了数据,但问题仍然存在。不知道为什么。
    • @vikrantnegi007 指调用saveData()后如何设置标志
    猜你喜欢
    • 2022-11-30
    • 2021-05-24
    • 2021-04-10
    • 2020-02-26
    • 2022-01-21
    • 1970-01-01
    • 1970-01-01
    • 2021-02-11
    • 2020-08-27
    相关资源
    最近更新 更多