导航生命周期
在上一节中,我们使用了一个具有两个屏幕(Home
和 Profile
)的堆栈导航器,并学习了如何使用 navigation.navigate('RouteName')
在屏幕之间导航。
¥In a previous section, we worked with a stack navigator that has two screens (Home
and Profile
) and learned how to use navigation.navigate('RouteName')
to navigate between the screens.
在这方面的一个重要问题是:当我们离开或回到 Home
时,会发生什么?屏幕如何发现用户正在离开或返回?
¥An important question in this context is: what happens with Home
when we navigate away from it, or when we come back to it? How does a screen find out that a user is leaving it or coming back to it?
如果你从 Web 背景进入 react-navigation,你可能会假设当用户从路由 A
导航到路由 B
时,A
将卸载(其 componentWillUnmount
被调用),并且当用户返回时 A
将再次挂载。虽然这些 React 生命周期方法仍然有效并在 React Navigation 中使用,但它们的用法与 Web 不同。这是由更复杂的移动导航需求驱动的。
¥If you are coming to react-navigation from a web background, you may assume that when the user navigates from route A
to route B
, A
will unmount (its componentWillUnmount
is called) and A
will mount again when the user comes back to it. While these React lifecycle methods are still valid and are used in React Navigation, their usage differs from the web. This is driven by the more complex needs of mobile navigation.
示例场景
¥Example scenario
考虑一个具有 2 个屏幕的堆栈导航器:Home
和 Profile
。当我们首次渲染导航器时,Home
屏幕已安装,即调用其 useEffect
或 componentDidMount
。当我们导航到 Profile
时,现在 Profile
已安装,并调用其 useEffect
或 componentDidMount
。但 Home
什么也没发生 - 它仍然挂载在堆栈中。不调用 useEffect
或 componentWillUnmount
返回的清理函数。
¥Consider a stack navigator with 2 screens: Home
and Profile
. When we first render the navigator, the Home
screen is mounted, i.e. its useEffect
or componentDidMount
is called. When we navigate to Profile
, now Profile
is mounted and its useEffect
or componentDidMount
is called. But nothing happens to Home
- it remains mounted in the stack. The cleanup function returned by useEffect
or componentWillUnmount
is not called.
当我们从 Profile
返回到 Home
时,Profile
已卸载,并调用其 useEffect
清理或 componentWillUnmount
。但 Home
未再次安装 - 它一直保持挂载状态 - 并且不调用其 useEffect
或 componentDidMount
。
¥When we go back from Profile
to Home
, Profile
is unmounted and its useEffect
cleanup or componentWillUnmount
is called. But Home
is not mounted again - it remained mounted the whole time - and its useEffect
or componentDidMount
is not called.
与其他导航器(结合)也可以观察到类似的结果。考虑一个带有两个选项卡的选项卡导航器,其中每个选项卡都是一个堆栈导航器:
¥Similar results can be observed (in combination) with other navigators as well. Consider a tab navigator with two tabs, where each tab is a stack navigator:
- Static
- Dynamic
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
的导航状态已保留!
¥We start on the HomeScreen
and navigate to DetailsScreen
. Then we use the tab bar to switch to the SettingsScreen
and navigate to ProfileScreen
. After this sequence of operations is done, all 4 of the screens are mounted! If you use the tab bar to switch back to the HomeStack
, you'll notice you'll be presented with the DetailsScreen
- the navigation state of the HomeStack
has been preserved!
React 导航生命周期事件
¥React Navigation lifecycle events
现在我们了解了 React 生命周期方法在 React Navigation 中的工作原理,让我们回答一开始提出的问题:"我们如何发现用户正在离开(模糊)它或返回它(焦点)?"
¥Now that we understand how React lifecycle methods work in React Navigation, let's answer the question we asked at the beginning: "How do we find out that a user is leaving (blur) it or coming back to it (focus)?"
React Navigation 向订阅这些事件的屏幕组件触发事件。我们可以监听 focus
和 blur
事件来分别了解屏幕何时进入焦点或失焦。
¥React Navigation emits events to screen components that subscribe to them. We can listen to focus
and blur
events to know when a screen comes into focus or goes out of focus respectively.
示例:
¥Example:
- Static
- Dynamic
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 使用的更多详细信息,请参阅 导航事件。
¥See Navigation events for more details on the available events and the API usage.
我们可以使用 useFocusEffect
钩子来执行副作用,而不是手动添加事件监听器。它就像 React 的 useEffect
钩子,但它与导航生命周期相关。
¥Instead of adding event listeners manually, we can use the useFocusEffect
hook to perform side effects. It's like React's useEffect
hook, but it ties into the navigation lifecycle.
示例:
¥Example:
- Static
- Dynamic
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
钩子,它返回一个布尔值来指示屏幕是否聚焦。
¥If you want to render different things based on if the screen is focused or not, you can use the useIsFocused
hook which returns a boolean indicating whether the screen is focused.
概括
¥Summary
-
虽然 React 的生命周期方法仍然有效,但 React Navigation 添加了更多事件,你可以通过
navigation
对象订阅这些事件。¥While React's lifecycle methods are still valid, React Navigation adds more events that you can subscribe to through the
navigation
object. -
你还可以使用
useFocusEffect
或useIsFocused
钩子。¥You may also use the
useFocusEffect
oruseIsFocused
hooks.