reactjs - 反应本机嵌套 AsyncStorage 调用未呈现
问题描述
我正在尝试使用 asyncstorage 库在反应本机页面上存储和显示随机数。基本上我希望代码显示一个随机数列表,前面是列表中的项目数,以及添加和删除随机数的按钮,如下所示:
几乎一切正常,我可以添加和删除随机数,并且 UI 将正确更新,但是,第一次呈现屏幕时(例如,当从另一个页面导航到时),没有显示任何数字,即使有多个保存. 我可以说它们已正确保存,因为第一个数字(列表中的随机数数量)显示正确,并且所有数字都在按下任一按钮后出现,而不是在初始渲染上。我最初认为这与嵌套的 asyncstorage 调用有关,但取消嵌套并添加 await 关键字,包装在 async 函数中提供了相同的结果。我在下面使用的方法是否有修复,或者我应该使用不同的方法来正确显示它。
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 建议的异步函数,但它们都产生了与我附加的代码相似的结果。
--EDIT 2 我能够解决这个问题,但我不知道它为什么会起作用。为了解决这个问题,我添加了一个新的任意常量:
const [testNum, setTestNum] = React.useState("");
然后将这段代码添加到我的 useeffect 函数的末尾:
AsyncStorage.setItem('@arbitrary',"arbitrary").then(resNum => {
setTestNum("arbitrary");
})
我从不使用或显示 testNum 变量,也不使用“任意”键做任何事情,我可以在那里放置任何东西,只要有存储调用和变量更新,它就可以工作。为什么这能解决我的问题?
解决方案
我有一些时间(和兴趣)来完成这个。完整的解决方案可以在这里找到和测试: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;
推荐阅读
- c - 如何在 platformio 上使用本地库构建项目?
- javascript - 如何从 LocalStorage Js 中获取特定值
- flutter - 我应该如何在另一个异步函数中使用异步未来列表?
- swift - 如何将 inputView 与 SwiftUI TextField 一起使用?
- node.js - 如何在节点js中提交表单后减少金额
- java - 原子操作如何工作以及线程如何不能被抢占。是 OS 保证还是 JVM 保证?
- r - R在满足3个条件的情况下比较一列中的行值
- javascript - 如何创建与缩略图不同的图像的轻量级 CSS 图像库?
- mysql - 是否可以在 MYSQL 中的查询之上运行排名函数
- java - 使用二叉搜索树的 TreeMap