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;
我正在尝试使用 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;