在多页面应用程序中,页面的跳转是经过路由或导航器来实现的。在RN应用开发过程当中,早期的路由能够直接使用官方提供的Navigator组件,不过从0.44版本开始,Navigator被官方从react native的核心组件库中剥离出来,放到react-native-deprecated-custom-components的模块中,而且Navigator组件也再也不被官方推荐使用。此时,咱们能够选择由React Native社区开源的一款路由导航库React Navigation。html
和以前的版本同样,React Navigation支持的导航类型有三种,分别是StackNavigator、TabNavigator和DrawerNavigator。前端
使用React Navigation以前,须要在你的React Native项目中安装所需的软件包,安装的方式分为npm和 yarn两种方式,以下所示。react
npm install @react-navigation/native //或者 yarn add @react-navigation/native
目前为止,咱们所安装的包都是导航的一些基础架构所须要的包,若是要建立不一样的导航栈效果,须要安装单独的插件宝。常见的有以下插件:react-native-gesture-handler, react-native-reanimated, react-native-screens 、react-native-safe-area-context 和@react-native-community/masked-view,而后运行以下命令安装插件。android
npm install react-native-reanimated react-native-gesture-handler react-native-screens react-native-safe-area-context @react-native-community/masked-view
须要的依赖库的做用以下所示:ios
官方文档中提到的 react-native-reanimated,该依赖在使用 DrawerNavigator 才用的到,不晓得为啥放到了总的安装文档中,尽可能都安装上。git
React Native 0.60 及其以上版本,不须要再运行react-native link
来安装原生库,若是没有连接上原生的库,也可使用下面的命令进行安装。github
npx pod-install ios
自从2019年《React Native移动开发实战 第2版》出版以后,我对RN的关注就比较少。而且因为更新了WebStrom好像以前安装的插件都失效了。web
在RN开发中,若是没有安装代码提示插件,编写代码是很是痛苦的,好在我使用以前的ReactNative.xml还能提示。安装ReactNative.xml的步骤以下:
1,下载ReactNative.xml,连接地址ReactNative.xml
2,而后/Users/mac/Library/Application Support/JetBrains/WebStorm2020.1/
目录下新建templates文件,并将上面下载的ReactNative.xml拷贝进去。
3,重启WebStrom,就能够看到提示了npm
在Web浏览器应用开发中,咱们可使用锚(标记)连接到不一样的页面,当用户单击连接时,URL被推到浏览器历史堆栈中。当用户按下返回按钮时,浏览器会从历史堆栈的顶部弹出之前访问过的页面。而在React Native开发中,咱们可使用React Navigation的堆栈导航器来管理页面的跳转和导航栈的管理。react-native
为了完成多页面的栈管理,咱们须要用到React Navigation提供的createStackNavigator
组件,而且使用前请确保安装了以下的插件。
npm install @react-navigation/stack
createStackNavigator是一个函数,它返回的对象包含两个属性,即屏幕和导航器,它们都是用于配置导航器的React组件。而NavigationContainer
是一个管理导航树并包含导航状态的组件,该组件必须包装全部导航器结构。
为此咱们新建一个管理路由页面的App.js文件,并添加以下代码:
// In App.js in a new project import * as React from 'react'; import { View, Text } from 'react-native'; import { NavigationContainer } from '@react-navigation/native'; import { createStackNavigator } from '@react-navigation/stack'; function HomeScreen() { return ( <View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}> <Text>Home Screen</Text> </View> ); } const Stack = createStackNavigator(); function App() { return ( <NavigationContainer> <Stack.Navigator> <Stack.Screen name="Home" component={HomeScreen} /> </Stack.Navigator> </NavigationContainer> ); } export default App;
而后,咱们修改RN项目的index.js文件的入口文件的路径为上面的App.js文件的路径,并运行项目,效果以下图所示。
全部的路由配置都会对应导航器上的属性,由于咱们尚未设置咱们导航器的任何属性,使用的都是默认的配置,如今咱们来为咱们的堆栈导航器添加第二个screen。
function DetailsScreen() { return ( <View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}> <Text>Details Screen</Text> </View> ); } const Stack = createStackNavigator(); function App() { return ( <NavigationContainer> <Stack.Navigator initialRouteName="Home"> <Stack.Screen name="Home" component={HomeScreen} /> <Stack.Screen name="Details" component={DetailsScreen} /> </Stack.Navigator> </NavigationContainer> ); }
如今咱们的路由栈中有两个路由,一个是Home另外一个是Details。咱们能够经过添加Screen组件的方式添加路由。Screen组件有一个name属性用来指定路由的名字,还有一个component属性,用来指定咱们将要渲染的组件。在上面的示例代码中,Home路由对应的是HomeScreen组件,Details路由对应的是DetailsScreen组件。
须要说明的是,component属性接收的是组件而不是一个render()方法。因此不要给它传递一个内联函数(好比component={()=><HomeScreen />}),不然你的组件在被卸载并再次加载的时候回丢失全部的state。
导航器中的每个路由页面均可以设置一些参数,例如title。
<Stack.Screen name="Home" component={HomeScreen} options={{ title: 'Overview' }}/>
若是要传递一些额外的属性,还可使用一个render函数做为组件的属性,以下所示。
<Stack.Screen name="Home"> {props => <HomeScreen {...props} extraData={someData} />} </Stack.Screen>
一个完整的应用一般是由多个路由构成的,而且路由之间能够相互跳转。在Web浏览器应用开发中,咱们可使用以下的方式实现页面的跳转。
<a href="details.html">Go to Details</a>
固然,也可使用以下的方式。
<a onClick={() => { window.location.href = 'details.html'; }} > Go to Details </a>
那对于React Navigation来讲,怎么跳转到一个新的页面呢?因为在React Navigation栈导航器中,navigation属性会被传递到每个screen组件中,因此咱们可使用以下的方式执行页面的跳转。
import * as React from 'react'; import { Button, View, Text } from 'react-native'; import { NavigationContainer } from '@react-navigation/native'; import { createStackNavigator } from '@react-navigation/stack'; function HomeScreen({ navigation }) { return ( <View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}> <Text>Home Screen</Text> <Button title="Go to Details" onPress={() => navigation.navigate('Details')} /> </View> ); } // ... other code from the previous section
在上面的代码中,咱们用到了navigation和navigate()方法。
运行上面的代码,运行效果以下图所示。
若是咱们调用navigation.navigate方法跳转到一个咱们没有在堆栈导航器中定义的路由的话会怎么呢,什么事情也不会发生。
若是咱们屡次跳转同一个路由页面会怎么样呢,以下所示。
function DetailsScreen({ navigation }) { return ( <View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}> <Text>Details Screen</Text> <Button title="Go to Details... again" onPress={() => navigation.navigate('Details')} /> </View> ); }
运行上面的代码,当你点击【Go to Details】时,什么事情都不会发生,这是由于咱们已经在Details页面了。
如今咱们假设一下,若是是咱们想要添加另外一个详情页面呢?这是一个很常见的场景,在这个场景中咱们想在同一个Details页面展现不一样的数据。为了实现这个 ,咱们能够用push()方法来替代navigate()方法,push()方法能够直接向堆栈中添加新的路由而无视当前导航历史。
<Button title="Go to Details... again" onPress={() => navigation.push('Details')} />
运行上面的代码,效果以下图所示。
堆栈导航器提供的导航头默认包含一个返回按钮,点击按钮能够返回到上一个页面,若是导航堆栈中只有一个页面,也就是说并无能够返回的页面的时候,这个回退按钮就不显示。
若是咱们使用的是自定义的导航头,可使用navigation.goBack()
方法来实现路由返回,以下所示。
function DetailsScreen({ navigation }) { return ( <View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}> <Text>Details Screen</Text> <Button title="Go to Details... again" onPress={() => navigation.push('Details')} /> <Button title="Go to Home" onPress={() => navigation.navigate('Home')} /> <Button title="Go back" onPress={() => navigation.goBack()} /> </View> ); }
另外一个常见的需求就是返回到多个页面。好比,若是你的堆栈中有多个页面,你想依次移除页面而后返回到第一个页面上。在上面的场景中,咱们想要返回Home页面,因此咱们可使用navigate('Home')
。
另外一种方法就是navigation.popToTop()
,这个方法将会返回到堆栈中的第一个页面上(初始页面),以下所示。
function DetailsScreen({ navigation }) { return ( <View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}> <Text>Details Screen</Text> <Button title="Go to Details... again" onPress={() => navigation.push('Details')} /> <Button title="Go to Home" onPress={() => navigation.navigate('Home')} /> <Button title="Go back" onPress={() => navigation.goBack()} /> <Button title="Go back to first screen in stack" onPress={() => navigation.popToTop()} /> </View> ); }
在页面跳转过程当中,免不了会进行数据的传递。在React Navigation中,路由之间传递参数主要有两种方式:
navigation.navigate
方法的第二个参数,从而实现路由跳转参数的传递。route.params()
方法读取传递过来的参数。无论是哪一种方式,咱们建议对须要传递对数据进行序列化,以下所示。
import * as React from 'react'; import { Text, View, Button } from 'react-native'; import { NavigationContainer } from '@react-navigation/native'; import { createStackNavigator } from '@react-navigation/stack'; function HomeScreen({ navigation }) { return ( <View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}> <Text>Home Screen</Text> <Button title="Go to Details" onPress={() => { /* 1. Navigate to the Details route with params */ navigation.navigate('Details', { itemId: 86, otherParam: 'anything you want here', }); }} /> </View> ); } function DetailsScreen({ route, navigation }) { /* 2. Get the param */ const { itemId } = route.params; const { otherParam } = route.params; return ( <View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}> <Text>Details Screen</Text> <Text>itemId: {JSON.stringify(itemId)}</Text> <Text>otherParam: {JSON.stringify(otherParam)}</Text> <Button title="Go to Details... again" onPress={() => navigation.push('Details', { itemId: Math.floor(Math.random() * 100), }) } /> <Button title="Go to Home" onPress={() => navigation.navigate('Home')} /> <Button title="Go back" onPress={() => navigation.goBack()} /> </View> ); } const Stack = createStackNavigator(); export default function App() { return ( <NavigationContainer> <Stack.Navigator> <Stack.Screen name="Home" component={HomeScreen} /> <Stack.Screen name="Details" component={DetailsScreen} /> </Stack.Navigator> </NavigationContainer> ); }
在路由页面上也能够更新这些参数,navigation.setParams()
方法就是用来更新这些参数的。固然,咱们也能够设置所传参数的默认值,当跳转页面时没有指定这些参数的值,那么他们就会使用默认值。参数的默认值能够在 initialParams属性中指定,以下所示。
<Stack.Screen name="Details" component={DetailsScreen} initialParams={{ itemId: 42 }}/>
咱们将将数据经过参数的方式传递给一个新的路由页面,也能够将数据回传给先前路由的页面。例如,有一个路由页面,该页面有一个建立帖子的按钮,这个按钮能够打开一个新的页面来建立帖子,在建立完帖子以后,须要想将帖子的一些数据回传给先前的页面。
对于这种需求,咱们可使用navigate来实现。若是路由已经在堆栈中存在了,他们他看起来就像goBack同样,而后只须要将数据绑定到nvigate的params回传给上一页便可,代码以下。
import * as React from 'react'; import { Text, TextInput, View, Button } from 'react-native'; import { NavigationContainer } from '@react-navigation/native'; import { createStackNavigator } from '@react-navigation/stack'; function HomeScreen({ navigation, route }) { React.useEffect(() => { if (route.params?.post) { // Post updated, do something with `route.params.post` // For example, send the post to the server } }, [route.params?.post]); return ( <View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}> <Button title="Create post" onPress={() => navigation.navigate('CreatePost')} /> <Text style={{ margin: 10 }}>Post: {route.params?.post}</Text> </View> ); } function CreatePostScreen({ navigation, route }) { const [postText, setPostText] = React.useState(''); return ( <> <TextInput multiline placeholder="What's on your mind?" style={{ height: 200, padding: 10, backgroundColor: 'white' }} value={postText} onChangeText={setPostText} /> <Button title="Done" onPress={() => { // Pass params back to home screen navigation.navigate('Home', { post: postText }); }} /> </> ); } const Stack = createStackNavigator(); export default function App() { return ( <NavigationContainer> <Stack.Navigator mode="modal"> <Stack.Screen name="Home" component={HomeScreen} /> <Stack.Screen name="CreatePost" component={CreatePostScreen} /> </Stack.Navigator> </NavigationContainer> ); }
当你点击【Done】按钮后,home页面的route.params将会被更新并刷新文本。
若是涉及有嵌套导航器的场景,则须要以稍微不一样的方式传递参数。例如,假设在Acount页面嵌套了一个设置页面,而且但愿将参数传递到该导航器中的设置页面,那么可使用下面的方式。
navigation.navigate('Account', { screen: 'Settings', params: { user: 'jane' }, });
React Navigation的Screen组件有一个options属性,这个属性包含了不少可配置的选项,例如设置导航栏的标题,以下所示。
function StackScreen() { return ( <Stack.Navigator> <Stack.Screen name="Home" component={HomeScreen} options={{ title: 'My home' }} /> </Stack.Navigator> ); }
为了在标题中使用参数,咱们能够把options属性定义为一个返回配置对象的方法。即咱们能够将options定义为一个方法,React Navigation调用它并为其传入两个可供使用的参数{navigation,route}
便可,以下所示。
function StackScreen() { return ( <Stack.Navigator> <Stack.Screen name="Home" component={HomeScreen} options={{ title: 'My home' }} /> <Stack.Screen name="Profile" component={ProfileScreen} options={({ route }) => ({ title: route.params.name })} /> </Stack.Navigator> ); }
传递到options方法中的参数是一个对象,该对象有两个属性navigation和route。
有时候,咱们须要在一个已加载的屏幕组件上更新它的options配置,对于这样的需求,咱们可使用navigation.setOptions
来实现。
/* Inside of render() of React class */ <Button title="Update the title" onPress={() => navigation.setOptions({ title: 'Updated!' })}/>
React Navigation支持自定义导航头的样式,咱们可使用以下三个样式属性 headerStyle、headerTintColor和headerTitleStyle,含义以下:
function StackScreen() { return ( <Stack.Navigator> <Stack.Screen name="Home" component={HomeScreen} options={{ title: 'My home', headerStyle: { backgroundColor: '#f4511e', }, headerTintColor: '#fff', headerTitleStyle: { fontWeight: 'bold', }, }} /> </Stack.Navigator> ); }
下面是演示示例的连接地址:Try the "header styles" example on Snack
自定义导航栏样式的时候,有如下几点须要注意:
有时候,咱们须要多个页面设置相同的导航头样式。那么,咱们可使用堆栈导航器的screenOptions属性。
function StackScreen() { return ( <Stack.Navigator screenOptions={{ headerStyle: { backgroundColor: '#f4511e', }, headerTintColor: '#fff', headerTitleStyle: { fontWeight: 'bold', }, }} > <Stack.Screen name="Home" component={HomeScreen} options={{ title: 'My home' }} /> </Stack.Navigator> ); }
在上面的例子中,任何属于StackScreen的页面都将使用相一样式的导航头。
有时候,咱们想对title作更多的设置,好比使用一张图片来替换title的文字,或者将title放进一个按钮中,这些场景中咱们彻底可使用自定义的组件来覆盖title,以下所示。
function LogoTitle() { return ( <Image style={{ width: 50, height: 50 }} source={require('@expo/snack-static/react-native-logo.png')} /> ); } function StackScreen() { return ( <Stack.Navigator> <Stack.Screen name="Home" component={HomeScreen} options={{ headerTitle: props => <LogoTitle {...props} /> }}/> </Stack.Navigator> ); }
一般,咱们可使用导航头的按钮来实现路由操做。好比,咱们点击导航栏左边的按钮能够返回上一个路由,点击导航头的右边能够进行其余操做,以下所示。
function StackScreen() { return ( <Stack.Navigator> <Stack.Screen name="Home" component={HomeScreen} options={{ headerTitle: props => <LogoTitle {...props} />, headerRight: () => ( <Button onPress={() => alert('This is a button!')} title="Info" color="#fff" /> ), }} /> </Stack.Navigator> ); }
咱们只须要使用navigation.setOptions
便可实现按钮与屏幕组件的交互,可使用navigation.setOptions
来访问screen组件的属性如,state和context等。
function StackScreen() { return ( <Stack.Navigator> <Stack.Screen name="Home" component={HomeScreen} options={({ navigation, route }) => ({ headerTitle: props => <LogoTitle {...props} />, })} /> </Stack.Navigator> ); } function HomeScreen({ navigation }) { const [count, setCount] = React.useState(0); React.useLayoutEffect(() => { navigation.setOptions({ headerRight: () => ( <Button onPress={() => setCount(c => c + 1)} title="Update count" /> ), }); }, [navigation]); return <Text>Count: {count}</Text>; }
createStackNavigator提供了平台默认的返回按钮,在IOS平台上,在按钮的旁边会有一个标签,这个标签会显示上一页标题的缩写,不然就是一个Back字样。
一样,开发者也能够能够经过设置headerBackTitle和headerTruncatedBackTitle来改变标签的行为,以下所示。
import { HeaderBackButton } from '@react-navigation/stack'; // ... <Screen name="Home" component={HomeScreen} options={{ headerLeft: (props) => ( <HeaderBackButton {...props} onPress={() => { // Do something }} /> ), }} />;
导航嵌套指的是一个导航器的导航页中又包含了另外一个导航器,例如。
function Home() { return ( <Tab.Navigator> <Tab.Screen name="Feed" component={Feed} /> <Tab.Screen name="Messages" component={Messages} /> </Tab.Navigator> ); } function App() { return ( <NavigationContainer> <Stack.Navigator> <Stack.Screen name="Home" component={Home} /> <Stack.Screen name="Profile" component={Profile} /> <Stack.Screen name="Settings" component={Settings} /> </Stack.Navigator> </NavigationContainer> ); }
在上述的例子中,Home组件包含了一个tab导航器。同时,这个Home组件也是堆栈导航器中的一个导航页,从而造成了一个导航嵌套的场景。
导航器的嵌套其实跟咱们一般使用的组件的嵌套相似,通常状况下,咱们一般会嵌套多个导航器。
实现导航器嵌套时,有如下几点须要特别注意:
好比,当你在一个被嵌套的堆栈导航器上点击返回按钮的时候,它会返回到本导航器(就是被嵌套的堆栈导航器)导航历史中的上一页,而不是返回到上级导航器中。
在Drawer导航器中嵌套了一个Stack 导航器,那么Drewer导航器的openDrawer、closeDrawer方法在被嵌套的stack导航器的navigation属性中依然是可用的。可是若是Stack导航器没有嵌套在Drawer导航器中,那么这些方法是不可访问的。
一样,若是你在Stack导航器中嵌套了一个Tab导航器,那么Tab导航器页面中的navigation属性会新获得push和replace这两个方法。
若是Stack导航器被嵌套在tTab导航器中,那么Stack导航器的页面不会响应由父Tab导航器触发的事件,好比咱们使用navigation.addListener
绑定的tabPress事件。为了可以响应父级导航器的事件,咱们可使用navigation.dangerouslyGetParent().addListener
来监听父级导航器的事件。
对于上面的这句话,咱们怎么理解呢?在Drawer导航器中嵌套了一个Stack导航器,你会看到抽屉的效果先被渲染出来,接着才是渲染Stack导航器的头部,可是若是将Drawer导航器嵌套在Stack导航器中,那么则会先渲染Stack导航器的头部再渲染抽屉效果。所以,咱们能够根据这一现象来确认咱们须要选择哪一种导航器的嵌套。
咱们先来看一下下面这段代码。
function Root() { return ( <Stack.Navigator> <Stack.Screen name="Profile" component={Profile} /> <Stack.Screen name="Settings" component={Settings} /> </Stack.Navigator> ); } function App() { return ( <NavigationContainer> <Drawer.Navigator> <Drawer.Screen name="Home" component={Home} /> <Drawer.Screen name="Root" component={Root} /> </Drawer.Navigator> </NavigationContainer> ); }
假如,如今想从Home页面中跳转到Root页面,那么咱们可使用下面的方式。
navigation.navigate('Root');
不过,有时间你可能但愿显示本身指定的页面,为了实现这个功能,能够在参数中携带页面的名字,以下所示。
navigation.navigate('Root',{screen:'Settings'});
固然,咱们也能够在页面跳转的时候传递一些参数,以下所示。
navigation.navigate('Root', { screen: 'Settings', params: { user: 'jane' }, });
当嵌套多个stack 或者drawer的导航栏时,子导航栏和父导航栏的标题都会显示出来。可是,一般更可取的作法是在子导航器中显示标题,并在堆栈导航器中隐藏标题。此时咱们可使用导航栏的headerShown: false
。
function Home() { return ( <NestedStack.Navigator> <NestedStack.Screen name="Profile" component={Profile} /> <NestedStack.Screen name="Settings" component={Settings} /> </NestedStack.Navigator> ); } function App() { return ( <NavigationContainer> <RootStack.Navigator mode="modal"> <RootStack.Screen name="Home" component={Home} options={{ headerShown: false }} /> <RootStack.Screen name="EditPost" component={EditPost} /> </RootStack.Navigator> </NavigationContainer> ); }
在前面的内容中,咱们介绍了React Navigation的一些基本用法,以及路由跳转相关的内容。可是,咱们并不知道路由是如何实现页面的跳转和返回的。
若是你对前端Web比较熟悉的话,那么你就会发现当用户从路由A跳转到路由B的时候,A将会被卸载(componentWillUnmount方法会被调用)当用户从其余页面返回A页面的时候,A页面又会被从新加载。React 的生命周期方法在React Navigation中仍然有效,不过相比Web的用法不尽相同,且移动端导航更为复杂。
假设有一个Stack导航器,里面A、B两个页面。当须要跳转到A页面的时候,它的componentDidMount将会被调用。当跳转到B页面的时候,B页面的compouentDidMount也会被调用,可是A页面依然在堆栈中保持被加载的状态,所以A页面的componentWillUnmount方法不会被调用。
可是,当咱们从B页面返回到A页面的时候,B页面的compouentWillUnmount则会被调用,可是A页面的componentDidMount不会被调用,由于其没有被卸载,在整个过程当中一直保持被加载的状态。
在其余类型的导航器中也是一样的结果,假设有一个Tab导航器,其有两个Tab,每一个Tab都是一个Stack导航器。
function App() { return ( <NavigationContainer> <Tab.Navigator> <Tab.Screen name="First"> {() => ( <SettingsStack.Navigator> <SettingsStack.Screen name="Settings" component={SettingsScreen} /> <SettingsStack.Screen name="Profile" component={ProfileScreen} /> </SettingsStack.Navigator> )} </Tab.Screen> <Tab.Screen name="Second"> {() => ( <HomeStack.Navigator> <HomeStack.Screen name="Home" component={HomeScreen} /> <HomeStack.Screen name="Details" component={DetailsScreen} /> </HomeStack.Navigator> )} </Tab.Screen> </Tab.Navigator> </NavigationContainer> ); }
如今,咱们从HomeScreen跳转到DetailsScreen页面,而后使用标签栏从SettingScreen 跳转到ProfileScreen。在这一系列操做作完后,全部的四个页面都已经被加载过了,若是你使用标签栏返回到HomeScreen,你会发现DetailsScreen页面到HomeStack的导航状态已经被保存。
咱们知道,React Native的页面都有本身的生命周期。而React Navigation则是经过将事件发送到订阅他们的页面组件中,并经过focus()和blur()方法监听用户离开返回事件,看下面一段代码。
function Profile({ navigation }) { React.useEffect(() => { const unsubscribe = navigation.addListener('focus', () => { // Screen was focused // Do something }); return unsubscribe; }, [navigation]); return <ProfileContent />; }
事实上,咱们不必手动添加监听器,能够直接使用useFocusEffect 挂钩来实现生命周期的监听,就像React的useEffect挂钩同样,不一样的是,useFocusEffect只能用户导航器的生命周期监听,以下所示。
import { useFocusEffect } from '@react-navigation/native'; function Profile() { useFocusEffect( React.useCallback(() => { // Do something when the screen is focused return () => { // Do something when the screen is unfocused // Useful for cleanup functions }; }, []) ); return <ProfileContent />; }
若是你想根据页面是否得到焦点和失去焦点来渲染不一样的东西,也能够调用useIsFocused 挂钩,useIsFocused会返回一个布尔值,用来指示该页面是否得到了焦点。
总的来讲,React 的生命周期方法仍然可用,除此以外,React Navigation又增长了更多的事件方法,开发者能够经过navigation属性来订阅他们。而且,开发者可使用useFocusEffect或者useIsFocused实现挂钩。
在RN中,Model是一个弹窗组件,它不是导航中的页面,所以其显示与隐藏都有其独特的方式,咱们可使用它展现一些特别的提示信息,以下图所示。
首先,咱们来看一下以下一段代码:
function HomeScreen({ navigation }) { return ( <View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}> <Text style={{ fontSize: 30 }}>This is the home screen!</Text> <Button onPress={() => navigation.navigate('MyModal')} title="Open Modal" /> </View> ); } function DetailsScreen() { return ( <View> <Text>Details</Text> </View> ); } function ModalScreen({ navigation }) { return ( <View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}> <Text style={{ fontSize: 30 }}>This is a modal!</Text> <Button onPress={() => navigation.goBack()} title="Dismiss" /> </View> ); } const MainStack = createStackNavigator(); const RootStack = createStackNavigator(); function MainStackScreen() { return ( <MainStack.Navigator> <MainStack.Screen name="Home" component={HomeScreen} /> <MainStack.Screen name="Details" component={DetailsScreen} /> </MainStack.Navigator> ); } function RootStackScreen() { return ( <RootStack.Navigator mode="modal"> <RootStack.Screen name="Main" component={MainStackScreen} options={{ headerShown: false }} /> <RootStack.Screen name="MyModal" component={ModalScreen} /> </RootStack.Navigator> ); }
首先,咱们来看一下上面示例中导航器的示意图。
在上面的示例中,咱们使用MainStackScreen做为RootStackScreen的屏幕组件,就至关于咱们在一个堆栈导航器中嵌入了另外一个堆栈导航器。所以,当咱们运行项目时,RootStackScreen会渲染一个堆栈导航器,该导航器有本身的头部,固然咱们也能够将这个头部隐藏。
堆栈导航器的model属性值能够是card(默认)或者model。在IOS上,model的展示方式是从底部滑入,退出的时候则是从上往下的方式来关闭它。在Android上,model是无效的属性,由于全面屏的model与android的平台自带的行为没有任何区别。
Model堆栈导航器在React Navigation导航中是很是有用的,若是你想变动堆栈导航器页面的过渡动画,那么就可使用mode属性。当将其设置为modal时,全部屏幕转换之间的过渡动画就会变为从底部滑到顶部,而不是原先的从右侧滑入。可是,须要注意的是,React Navigation的modal会对整个堆栈导航器起效,因此为了在其余屏幕上使用从右侧滑入的效果,咱们能够再另外添加一个导航器,这个导航器使用默认的配置就行。
Navigator是一个React组件,它决定应用以哪一种方式展示已定义的页面。NavigationContainer则是一个管理导航树并包含导航状态的组件,该组件是全部导航器组件的父容器,包装了全部导航器的结构。一般,咱们须要将NavigationContainer组件放在应用程序的根目录下,做为启动的根文件。
function App() { return ( <NavigationContainer> <Stack.Navigator> // <---- This is a Navigator <Stack.Screen name="Home" component={HomeScreen} /> </Stack.Navigator> </NavigationContainer> ); }
Router便是路由器,它是一组函数,它决定如何在导航器中处理用户的操做和状态的更改,一般,开发者不须要直接参与路由器交互,除非您须要编写自定义导航器。
每一个路由都是一个对象,其中包含标识它的键和指定路由类型的“名称”,而且还能够包含任意类型的参数。
{ key: 'B', name: 'Profile', params: { id: '123' } }
Screen component即屏幕组件,是咱们在路由配置中须要跳转的组件。
const Stack = createStackNavigator(); const StackNavigator = ( <Stack.Navigator> <Stack.Screen name="Home" component={HomeScreen} /> <Stack.Screen name="Details" component={DetailsScreen} /> </Stack.Navigator> );
须要注意的是,只有当屏幕被React Navigation呈现为一个路由时才能实现跳转功能。例如,若是咱们渲染DetailsScreen做为主屏幕的子元素,那么DetailsScreen将不会提供导航道具,当你在主屏幕上按下“Go to Details…”按钮时应用程序会抛出【未定义该对象】”的错误。
function HomeScreen() { return ( <View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}> <Text>Home Screen</Text> <Button title="Go to Details" onPress={() => navigation.navigate('Details')} /> <DetailsScreen /> </View> ); }
经过使用Navigation提供的属性,咱们能够在两个页面之间传递数据,经常使用的属性以下。
导航器的状态以下所示。
{ key: 'StackRouterRoot', index: 1, routes: [ { key: 'A', name: 'Home' }, { key: 'B', name: 'Profile' }, ] }