在useEffect挂钩中与AsyncStorage一起设置状态会导致无限循环吗?

时间:2020-02-20 04:51:27

标签: reactjs react-native react-hooks asyncstorage

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

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

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中删除了[],它不会循环播放,但是我的渲染数据落后了一步。因此,每当我保存数据时,它不会显示当前数据,而是显示前一个数据。

关于我在这里做错什么的任何解释,我该如何解决?

谢谢。

2 个答案:

答案 0 :(得分:5)

正如您已经提到的,无限循环是由于您实际上将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();
};

答案 1 :(得分:3)

只需添加条件标志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]);