我在react方面几乎没有任何知识,但我正在边学习边学习。我在学校学习了基本的类组件(经典的React(,但现在我正在深入研究Hooks API(主要是因为我发现它更容易学习和管理,尽管似乎有更多关于异步行为的技巧(。所以我的问题可能看起来很傻。
我在同一主题上发现了这个关于setState行为的线程,但这是关于类组件的。
在我当前的应用程序中,我试图使用事件处理程序设置三种不同的状态。最后一个状态似乎是立即设置的,而其他两个状态在变为实数之前保持一段时间未定义。我正在使用React Native组件进行移动开发,因此您将在代码中看到一些片段,如<SafeAreaView>
。
export default App = () => {
const [ destLong, setDestLong ] = useState();
const [ destLat, setDestLat ] = useState();
const [ startNav, setStartNav ] = useState(false);
const [ locations, setLocations ] = useState([
{
name: 'IKEA',
long: '-74.00653395444186',
lat: '40.68324646680103',
},
{
name: 'JFK Intl. Airport',
long: '-73.78131423688552',
lat: '40.66710279890186',
},
{
name: 'Microcenter',
long: '-74.00516039699959',
lat: '40.67195933297655',
}
]);
const startNavigation = (goinglong, goinglat) => {
setDestLong(goinglong);
setDestLat(goinglat);
setStartNav(true);
}
return (
<SafeAreaView style={styles.container}>
{ startNav ?
<MapView
destLong = {destLong}
destLat = {destLat}
/>
:
<View style={styles.buttonContainer}>
<ScrollView>
{
locations.map((location, i) => {
return(
<Card
style={styles.card}
key={i}
title={ location.name }
iconName="home"
iconType="Entypo"
description={ location.long + ", " + location.lat }
onPress={() => startNavigation(location.long, location.lat)}
/>
);
})
}
</ScrollView>
</View>
}
</SafeAreaView>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
},
buttonContainer: {
width: '100%',
height: '100%',
justifyContent: 'center',
alignItems: 'center'
},
logo: {
width: '50%',
height: '50%',
resizeMode: 'contain'
},
card: {
marginBottom: 10,
}
});
这引发了一个错误,因为MapView期望destLong
和destLat
正确渲染。当我在startNavigation函数中控制台日志时,它似乎会立即将startNav
的状态更新为true onPress,但destLong
和destLat
在设置之前的几个周期内保持未定义状态。
我尝试了一种不同的方法:
useEffect(() => {
setStartNav(true);
}, [destLong]);
const startNavigation = (goinglong, goinglat) => {
setDestLong(goinglong);
setDestLat(goinglat);
}
但它只是崩溃了应用程序(我猜是无限循环(。
我还尝试过完全删除startNav
状态,并在destLong
上呈现类似的<MapView>
{ destLong ?
<MapView
destLong = {destLong}
destLat = {destLat}
/>
:
<View style={styles.buttonContainer}>
...
</View>
}
但这也没有奏效。
这就引出了一个问题:Hooks API是尊重setState的顺序,还是彼此异步执行?据我所知是后者。但是,你是如何处理这种行为的呢?
我也在这里添加我的注释,因为我无法为上面的注释添加正确的格式。
通过useState
设置状态实际上是异步的,或者更确切地说,状态更改是排队的,然后它将在重新渲染后返回其新值。这意味着无法保证各州的设置顺序。它们会按顺序发射,但可能不会按相同的顺序设置。
您可以在此处阅读更多信息:https://dev.to/shareef/react-usestate-hook-is-asynchronous-1hia,以及这里https://blog.logrocket.com/a-guide-to-usestate-in-react-ecb9952e406c/#reacthooksupdatestate
在你的情况下,我会像这样使用useState
和useEffect
:
useEffect(() => {
if(destLong && destLat && !startNav) {
setStartNav(true);
}
}, [destLong, destLat, startNav]);
const startNavigation = (goinglong, goinglat) => {
setDestLong(goinglong);
setDestLat(goinglat);
}
话虽如此,我认为您可以通过完全省略startNav状态来进一步简化代码,并更新您的条件呈现:
{ (destLat && destLong) ?
<MapView
destLong = {destLong}
destLat = {destLat}
/>
:
<View style={styles.buttonContainer}>
...
</View>
}
以上内容应该具有相同的效果,因为您有两个状态,一开始是未定义的,当它们都被定义时,您想要呈现一些东西并使用它们的值。
如果您想再次显示选项,可以通过执行setDestLat(undefined)
和setDestLong(undefined)
再次将状态设置为未定义