React Native 嵌套 AsyncStorage 调用不呈现

React Native Nested AsyncStorage Call Not Rendering

我正在尝试使用 asyncstorage 库在 React Native 页面上存储和显示随机数。基本上我希望代码显示一个随机数列表,前面是列表中的项目数,以及添加和删除随机数的按钮,如下所示:

几乎一切正常,我可以添加和删除随机数,并且 UI 会正确更新,但是,第一次呈现屏幕时(例如,从另一个页面导航到时),没有数字显示,即使有多个保存。我可以看出它们已正确保存,因为第一个数字(列表中随机数的数量)显示正确,并且所有数字都在按下任一按钮后出现,只是不在初始渲染中。我最初认为它与嵌套的 asyncstorage 调用有关,但是取消嵌套它们并添加 await 关键字,包装在异步函数中提供了相同的结果。我在下面使用的方法是否有修复,或者我应该使用不同的方法来正确显示它。

const [loopNum, setLoopNum] = React.useState(0);
      const [savedNumbers, setSavedNumbers] = React.useState<any>([]);
    React.useEffect(() => {
    AsyncStorage.getItem('@loopNum').then(res => {
          if (res !== undefined && res !== null && !isNaN(parseInt(res,10))){
            setLoopNum(parseInt(res,10));
            let rows = savedNumbers;
            for (let i = 0; i < parseInt(res,10); i++){
              AsyncStorage.getItem('@number' + i).then(resInner => {
                rows.push(<Text>Number {resInner}</Text>);
              })
            }
            setSavedNumbers(rows);
          }else{
            setLoopNum(0);
          }
        })
      })
    const addNum = () => {
        const value =  Math.random().toString();
        AsyncStorage.setItem('@number' + loopNum, value).then(res => {
          let _temp = savedNumbers;
          _temp.push(<Text>Number {value}</Text>);
          setSavedNumbers(_temp);
          AsyncStorage.setItem('@loopNum', (loopNum+1).toString()).then(resNum => {
            setLoopNum(loopNum+1);
          })
          
      })
    }
      const removeNum = () => {
      AsyncStorage.removeItem('@number' + loopNum).then(res => {
          let _temp = savedNumbers;
          _temp.pop();
          setSavedNumbers(_temp);
          AsyncStorage.setItem('@loopNum', (loopNum-1).toString()).then(resNum => {
            setLoopNum(loopNum-1);
          })
          
      })
    }
      return (
        <View style={styles.container}>
          <Button
            onPress={addNum}
            title="Add"
            color="#841584"/>
          <Button
            onPress={removeNum}
            title="Remove"
            color="#841584"/>
          <Text>{loopNum}</Text>
          <View>{savedNumbers}</View>
        </View>
      );

--编辑 我尝试了多种使用异步函数的方法,就像@Nikita 建议的那样,但它们都产生了与我附加的代码相似的结果。

--编辑 2 我能够解决这个问题,但我不知道为什么会这样。为了解决这个问题,我添加了一个新的任意常量:

const [testNum, setTestNum] = React.useState("");

然后将这段代码添加到我的 useeffect 函数的末尾:

AsyncStorage.setItem('@arbitrary',"arbitrary").then(resNum => {
      setTestNum("arbitrary");
}) 

我从不使用或显示 testNum 变量,也不对 'arbitrary' 键做任何事情,我可以在那里放置任何东西,只要有存储调用和变量更新,它就可以工作。为什么这可以解决我的问题?

AsycStorage.getItem(string) 是一个异步函数。 所以请编写如下代码:

(async() => {
try {
  const value = await AsyncStorage.getItem('@loopNum');
  if(value != null) {
   .....
  }
} catch(e) {
  .....
}
})();

我有一些时间(和兴趣)来完成这个。可以在此处找到并测试完整的解决方案:https://snack.expo.dev/@zvona/asyncstorage-so-problem.

有很多变化,但我会在底部添加整个代码,以防某天 Expo Snack 消失。

关键点:

  • 您指的是原始(规定的)数组,代码如下:let rows = savedNumbers;。它不是在创建新实例,而是直接改变现有实例。这导致了一些错误
  • async/await 模式确实工作得更好并且产生更清晰的代码
  • 您应该将功能行为和与 DB (AsyncStorage) 的交互与表示分开。因此,我创建了一个完全独立的函数来填充存储中的内容。
import * as React from 'react';
import { Button, Text, View, StyleSheet } from 'react-native';
import AsyncStorage from '@react-native-async-storage/async-storage';

const App = () => {
  const [loopNum, setLoopNum] = React.useState(0);
  const [savedNumbers, setSavedNumbers] = React.useState([]);

  const initiate = async () => {
    const currentLoopNum = (await AsyncStorage.getItem('@loopNum')) || '0';
    const numbers = [];

    for (let i = 0; i < parseInt(currentLoopNum, 10); i++) {
      const savedNumber = await getSavedNumber(i);
      numbers.push(savedNumber);
    }

    setLoopNum(parseInt(currentLoopNum, 10));
    setSavedNumbers(numbers);
  };

  React.useEffect(() => {
    initiate();
  }, []);

  const getSavedNumber = async (i) => {
    const num = await AsyncStorage.getItem('@number' + i);
    return num;
  };

  const populateSavedNumbers = () => 
    savedNumbers.map((num) => <Text>{`Number: ${num}`}</Text>);

  const addNum = async () => {
    const value = Math.random().toString();
    const newLoopNum = loopNum + 1;
    
    await AsyncStorage.setItem(`@number${loopNum}`, value);
    await AsyncStorage.setItem('@loopNum', newLoopNum);

    setSavedNumbers([...savedNumbers, value]);
    setLoopNum(newLoopNum);
  };

  const removeNum = async () => {
    const newNumbers = savedNumbers.slice(0, -1);
    const newLoopNum = loopNum - 1;

    await AsyncStorage.removeItem(`@number${loopNum}`);
    await AsyncStorage.setItem('@loopNum', newLoopNum);

    setSavedNumbers(newNumbers);
    setLoopNum(newLoopNum);
  };

  return (
    <View style={styles.container}>
      <Button onPress={addNum} title="Add" color="#841584" />
      <Button onPress={removeNum} title="Remove" color="#841584" />
      <Text>{loopNum}</Text>
      <View>{populateSavedNumbers()}</View>
    </View>
  );
};

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    backgroundColor: '#ecf0f1',
    padding: 8,
  },
});

export default App;