导航事件
你可以监听 React Navigation 发出的各种事件,以获取特定事件的通知,并且在某些情况下,可以覆盖默认操作。有一些核心事件,例如 focus
、blur
等(如下所述),它们适用于每个导航器,以及仅适用于某些导航器的特定于导航器的事件。
除了核心事件之外,每个导航器都可以发出他们自己的自定义事件。例如,堆栈导航器发出 transitionStart
和 transitionEnd
事件,标签导航器发出 tabPress
事件等。你可以在各个导航器的文档中找到有关发出事件的详细信息。
核心事件
以下是每个导航器中可用的事件
focus
当屏幕进入焦点时,会发出此事件。
对于大多数情况,useFocusEffect
hook 可能比手动添加监听器更合适。请参阅本指南以获取更多详细信息,以决定应使用哪个 API。
blur
当屏幕失去焦点时,会发出此事件。
state
当导航器的状态更改时,会发出此事件。此事件在事件数据 (event.data.state
) 中接收导航器的状态。
beforeRemove
当用户由于导航操作而离开屏幕时,会发出此事件。可以通过在事件监听器中调用 e.preventDefault()
来阻止用户离开屏幕。
React.useEffect(
() =>
navigation.addListener('beforeRemove', (e) => {
if (!hasUnsavedChanges) {
return;
}
// Prevent default behavior of leaving the screen
e.preventDefault();
// Prompt the user before leaving the screen
Alert.alert(
'Discard changes?',
'You have unsaved changes. Are you sure to discard them and leave the screen?',
[
{
text: "Don't leave",
style: 'cancel',
onPress: () => {
// Do nothing
},
},
{
text: 'Discard',
style: 'destructive',
// If the user confirmed, then we dispatch the action we blocked earlier
// This will continue the action that had triggered the removal of the screen
onPress: () => navigation.dispatch(e.data.action),
},
]
);
}),
[navigation, hasUnsavedChanges]
);
在此事件中阻止操作无法与 @react-navigation/native-stack
正确配合使用。我们建议改用 usePreventRemove
hook。
监听事件
有多种方法可以监听来自导航器的事件。注册为事件监听器的每个回调都会接收一个事件对象作为其参数。事件对象包含一些属性
data
- 导航器传递的有关事件的其他数据。如果没有传递数据,则可能为undefined
。target
- 应该接收事件的屏幕的路由键。对于某些事件,如果事件与特定屏幕无关,则可能为undefined
。preventDefault
- 对于某些事件,事件对象上可能有一个preventDefault
方法。调用此方法将阻止事件执行的默认操作(例如切换tabPress
上的标签)。仅某些事件(例如tabPress
)支持阻止操作,并且不适用于所有事件。
你可以使用以下 API 监听事件
navigation.addListener
在屏幕内部,你可以使用 addListener
方法在 navigation
对象上添加监听器。addListener
方法接受 2 个参数:事件的类型和事件发生时要调用的回调。它返回一个可以调用的函数来取消订阅事件。
示例
const unsubscribe = navigation.addListener('tabPress', (e) => {
// Prevent default action
e.preventDefault();
});
通常,你会在函数组件的 React.useEffect
中添加事件监听器。例如
- 静态
- 动态
function ProfileScreen() {
const navigation = useNavigation();
React.useEffect(() => {
const unsubscribe = navigation.addListener('focus', () => {
// Screen was focused
});
return unsubscribe;
}, [navigation]);
React.useEffect(() => {
const unsubscribe = navigation.addListener('blur', () => {
// Screen was unfocused
});
return unsubscribe;
}, [navigation]);
// Rest of the component
}
function ProfileScreen({ navigation }) {
React.useEffect(() => {
const unsubscribe = navigation.addListener('focus', () => {
// Screen was focused
});
return unsubscribe;
}, [navigation]);
React.useEffect(() => {
const unsubscribe = navigation.addListener('blur', () => {
// Screen was unfocused
});
return unsubscribe;
}, [navigation]);
// Rest of the component
}
unsubscribe
函数可以作为 effect 中的清理函数返回。
对于类组件,你可以在 componentDidMount
生命周期方法中添加事件,并在 componentWillUnmount
中取消订阅
class Profile extends React.Component {
componentDidMount() {
this._unsubscribe = navigation.addListener('focus', () => {
// do something
});
}
componentWillUnmount() {
this._unsubscribe();
}
render() {
// Content of the component
}
}
需要记住的一件事是,你只能使用 addListener
监听来自直接导航器的事件。例如,如果你尝试在嵌套在标签内的堆栈内的屏幕中添加监听器,它将不会获得 tabPress
事件。如果需要监听来自父导航器的事件,你可以使用 navigation.getParent
获取对父屏幕的 navigation 对象的引用并添加监听器。
const unsubscribe = navigation
.getParent('MyTabs')
.addListener('tabPress', (e) => {
// Do something
});
这里 'MyTabs'
指的是你在你想要监听其事件的父 Tab.Navigator
的 id
prop 中传递的值。
Screen
上的 listeners
prop
有时你可能想从定义导航器的组件而不是屏幕内部添加监听器。你可以使用 Screen
组件上的 listeners
prop 添加监听器。listeners
prop 接受一个对象,其中事件名称作为键,监听器回调作为值。
示例
- 静态
- 动态
const Tab = createBottomTabNavigatior({
screens: {
Chat: {
screen: Chat,
listeners: {
tabPress: (e) => {
// Prevent default action
e.preventDefault;
},
},
},
},
});
<Tab.Screen
name="Chat"
component={Chat}
listeners={{
tabPress: (e) => {
// Prevent default action
e.preventDefault();
},
}}
/>
你还可以传递一个回调函数,该函数返回带有监听器的对象。它将接收 navigation
和 route
作为参数。
示例
- 静态
- 动态
const Tab = createBottomTabNavigatior({
screens: {
Chat: {
screen: Chat,
listeners: ({ navigation, route }) => ({
tabPress: (e) => {
// Prevent default action
e.preventDefault;
// Do something with the `navigation` object
navigation.navigate('AnotherPlace');
},
}),
},
},
});
<Tab.Screen
name="Chat"
component={Chat}
listeners={({ navigation, route }) => ({
tabPress: (e) => {
// Prevent default action
e.preventDefault();
// Do something with the `navigation` object
navigation.navigate('AnotherPlace');
},
})}
/>
导航器上的 screenListeners
prop
你可以将名为 screenListeners
的 prop 传递给导航器组件,你可以在其中为来自此导航器的所有屏幕的事件指定监听器。如果你想监听特定事件而不管屏幕如何,或者想监听所有屏幕发出的常见事件(例如 state
),这将非常有用。
示例
- 静态
- 动态
const Stack = createNativeStackNavigator({
screenListeners: {
state: (e) => {
// Do something with the state
console.log('state changed', e.data);
},
},
screens: {
Home: HomeScreen,
Profile: ProfileScreen,
},
});
<Stack.Navigator
screenListeners={{
state: (e) => {
// Do something with the state
console.log('state changed', e.data);
},
}}
>
<Stack.Screen name="Home" component={HomeScreen} />
<Stack.Screen name="Profile" component={ProfileScreen} />
</Stack.Navigator>
与 listeners
类似,你还可以将函数传递给 screenListeners
。该函数将接收每个屏幕的 navigation
对象 和 route
对象。如果你需要访问 navigation
对象,这将非常有用。
- 静态
- 动态
const Tab = createBottomTabNavigatior({
screenListeners: ({ navigation }) => ({
state: (e) => {
// Do something with the state
console.log('state changed', e.data);
// Do something with the `navigation` object
if (!navigation.canGoBack()) {
console.log("we're on the initial screen");
}
},
}),
screens: {
Home: HomeScreen,
Profile: ProfileScreen,
},
});
<Tab.Navigator
screenListeners={({ navigation }) => ({
state: (e) => {
// Do something with the state
console.log('state changed', e.data);
// Do something with the `navigation` object
if (!navigation.canGoBack()) {
console.log("we're on the initial screen");
}
},
})}
>
<Tab.Screen name="Home" component={HomeScreen} />
<Tab.Screen name="Profile" component={ProfileScreen} />
</Tab.Navigator>