导航生命周期
在上一节中,我们使用了带有两个屏幕(Home
和 Profile
)的堆栈导航器,并学习了如何使用 navigation.navigate('RouteName')
在屏幕之间导航。
在这种情况下,一个重要的问题是:当我们离开 Home
屏幕或返回到它时,Home
屏幕会发生什么?屏幕如何知道用户正在离开它或返回到它?
如果您是从 Web 背景转到 react-navigation,您可能会认为当用户从路由 A
导航到路由 B
时,A
将卸载(其 componentWillUnmount
被调用),并且当用户返回到它时,A
将再次挂载。虽然这些 React 生命周期方法仍然有效并在 React Navigation 中使用,但它们的用法与 Web 不同。这是由移动导航更复杂的需求驱动的。
示例场景
考虑一个带有 2 个屏幕的堆栈导航器:Home
和 Profile
。当我们首次渲染导航器时,Home
屏幕被挂载,即其 useEffect
或 componentDidMount
被调用。当我们导航到 Profile
时,现在 Profile
被挂载,并且其 useEffect
或 componentDidMount
被调用。但是 Home
什么也没发生 - 它仍然挂载在堆栈中。useEffect
或 componentWillUnmount
返回的清理函数不会被调用。
当我们从 Profile
返回到 Home
时,Profile
被卸载,并且其 useEffect
清理或 componentWillUnmount
被调用。但是 Home
不会再次挂载 - 它一直保持挂载状态 - 并且其 useEffect
或 componentDidMount
不会被调用。
与其他导航器(组合使用)也可以观察到类似的结果。考虑一个带有两个标签的标签导航器,其中每个标签都是一个堆栈导航器
- 静态
- 动态
const SettingsStack = createNativeStackNavigator({
screens: {
Settings: SettingsScreen,
Profile: ProfileScreen,
},
});
const HomeStack = createNativeStackNavigator({
screens: {
Home: HomeScreen,
Details: DetailsScreen,
},
});
const MyTabs = createBottomTabNavigator({
screenOptions: {
headerShown: false,
},
screens: {
First: SettingsStack,
Second: HomeStack,
},
});
function FirstScreen() {
return (
<SettingsStack.Navigator>
<SettingsStack.Screen name="Settings" component={SettingsScreen} />
<SettingsStack.Screen name="Profile" component={ProfileScreen} />
</SettingsStack.Navigator>
);
}
function SecondScreen() {
return (
<HomeStack.Navigator>
<HomeStack.Screen name="Home" component={HomeScreen} />
<HomeStack.Screen name="Details" component={DetailsScreen} />
</HomeStack.Navigator>
);
}
function Root() {
return (
<MyTabs.Navigator screenOptions={{ headerShown: false }}>
<MyTabs.Screen name="First" component={FirstScreen} />
<MyTabs.Screen name="Second" component={SecondScreen} />
</MyTabs.Navigator>
);
}
我们从 HomeScreen
开始导航到 DetailsScreen
。然后我们使用标签栏切换到 SettingsScreen
并导航到 ProfileScreen
。在此操作序列完成后,所有 4 个屏幕都已挂载!如果您使用标签栏切换回 HomeStack
,您会注意到您将看到 DetailsScreen
- HomeStack
的导航状态已保留!
React Navigation 生命周期事件
现在我们了解了 React 生命周期方法在 React Navigation 中的工作方式,让我们回答我们在开始时提出的问题:“我们如何知道用户正在离开(blur)它或返回到它(focus)?”
React Navigation 向订阅它们的屏幕组件发出事件。我们可以监听 focus
和 blur
事件,以了解屏幕何时进入焦点或失去焦点。
示例
- 静态
- 动态
function ProfileScreen() {
const navigation = useNavigation();
React.useEffect(() => {
const unsubscribe = navigation.addListener('focus', () => {
console.log('ProfileScreen focused');
});
return unsubscribe;
}, [navigation]);
React.useEffect(() => {
const unsubscribe = navigation.addListener('blur', () => {
console.log('ProfileScreen blurred');
});
return unsubscribe;
}, [navigation]);
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text>Profile Screen</Text>
</View>
);
}
function ProfileScreen() {
const navigation = useNavigation();
React.useEffect(() => {
const unsubscribe = navigation.addListener('focus', () => {
console.log('ProfileScreen focused');
});
return unsubscribe;
}, [navigation]);
React.useEffect(() => {
const unsubscribe = navigation.addListener('blur', () => {
console.log('ProfileScreen blurred');
});
return unsubscribe;
}, [navigation]);
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text>Profile Screen</Text>
</View>
);
}
有关可用事件和 API 用法的更多详细信息,请参阅导航事件。
除了手动添加事件监听器之外,我们可以使用 useFocusEffect
Hook 来执行副作用。它类似于 React 的 useEffect
Hook,但它与导航生命周期相关联。
示例
- 静态
- 动态
import { useFocusEffect } from '@react-navigation/native';
function ProfileScreen() {
useFocusEffect(
React.useCallback(() => {
// Do something when the screen is focused
console.log('ProfileScreen focus effect');
return () => {
// Do something when the screen is unfocused
// Useful for cleanup functions
console.log('ProfileScreen focus effect cleanup');
};
}, [])
);
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text>Profile Screen</Text>
</View>
);
}
import { useFocusEffect } from '@react-navigation/native';
function ProfileScreen() {
useFocusEffect(
React.useCallback(() => {
// Do something when the screen is focused
console.log('ProfileScreen focus effect');
return () => {
// Do something when the screen is unfocused
// Useful for cleanup functions
console.log('ProfileScreen focus effect cleanup');
};
}, [])
);
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text>Profile Screen</Text>
</View>
);
}
如果您想根据屏幕是否聚焦来渲染不同的内容,则可以使用 useIsFocused
Hook,它返回一个布尔值,指示屏幕是否聚焦。
总结
- 虽然 React 的生命周期方法仍然有效,但 React Navigation 添加了更多事件,您可以通过
navigation
对象订阅这些事件。 - 您也可以使用
useFocusEffect
或useIsFocused
Hook。