使用Hooks API:React是否尊重setState顺序



我在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期望destLongdestLat正确渲染。当我在startNavigation函数中控制台日志时,它似乎会立即将startNav的状态更新为true onPress,但destLongdestLat在设置之前的几个周期内保持未定义状态。

我尝试了一种不同的方法:

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

在你的情况下,我会像这样使用useStateuseEffect

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)再次将状态设置为未定义

最新更新