最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • React Navigation 5.x详解

    正文概述 掘金(xiangzhihong)   2020-12-02   630

    一、 React Navigation简介

    在多页面应用程序中,页面的跳转是通过路由或导航器来实现的。在RN应用开发过程中,早期的路由可以直接使用官方提供的Navigator组件,不过从0.44版本开始,Navigator被官方从react native的核心组件库中剥离出来,放到react-native-deprecated-custom-components的模块中,并且Navigator组件也不再被官方推荐使用。此时,我们可以选择由React Native社区开源的一款路由导航库React Navigation。

    和之前的版本一样,React Navigation支持的导航类型有三种,分别是StackNavigator、TabNavigator和DrawerNavigator。

    • StackNavigator:包含导航栏的页面导航组件,类似于官方的Navigator组件。
    • TabNavigator:底部展示tabBar的页面导航组件。
    • DrawerNavigator:用于实现侧边栏抽屉页面的导航组件。

    1.1 安装

    使用React Navigation之前,需要在你的React Native项目中安装所需的软件包,安装的方式分为npm和 yarn两种方式,如下所示。

    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,然后运行如下命令安装插件。

    npm install react-native-reanimated react-native-gesture-handler react-native-screens react-native-safe-area-context @react-native-community/masked-view
    

    需要的依赖库的作用如下所示:

    • react-native-gesture-handler:用于手势切换页面。
    • react-native-screens:用于原生层释放未展示的页面,改善 app 内存使用。
    • react-native-safe-area-context: 用于保证页面显示在安全区域(主要针对刘海屏)。
    • @react-native-community/masked-view:用在头部导航栏中返回按钮的颜色设置。
    • @react-navigation/native 为 React Navigation 的核心。

    官方文档中提到的 react-native-reanimated,该依赖在使用 DrawerNavigator 才用的到,不晓得为啥放到了总的安装文档中,尽量都安装上。

    React Native 0.60 及其以上版本,不需要再运行react-native link 来安装原生库,如果没有链接上原生的库,也可以使用下面的命令进行安装。

    npx pod-install ios
    

    1.2 RN的提示插件

    自从2019年《React Native移动开发实战 第2版》出版之后,我对RN的关注就比较少。并且由于更新了WebStrom好像之前安装的插件都失效了。

    在RN开发中,如果没有安装代码提示插件,编写代码是非常痛苦的,好在我使用之前的ReactNative.xml还能提示。安装ReactNative.xml的步骤如下: 1,下载ReactNative.xml,链接地址ReactNative.xml 2,然后/Users/mac/Library/Application Support/JetBrains/WebStorm2020.1/目录下新建templates文件,并将上面下载的ReactNative.xml拷贝进去。 3,重启WebStrom,就可以看到提示了

    React Navigation 5.x详解

    二、 Hello React Navigation

    在Web浏览器应用开发中,我们可以使用锚(标记)链接到不同的页面,当用户单击链接时,URL被推到浏览器历史堆栈中。当用户按下返回按钮时,浏览器会从历史堆栈的顶部弹出以前访问过的页面。而在React Native开发中,我们可以使用React Navigation的堆栈导航器来管理页面的跳转和导航栈的管理。

    为了完成多页面的栈管理,我们需要用到React Navigation提供的createStackNavigator组件,并且使用前请确保安装了如下的插件。

    npm install @react-navigation/stack
    

    2.1 堆栈导航器

    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文件的路径,并运行项目,效果如下图所示。 React Navigation 5.x详解

    2.2 配置导航器

    所有的路由配置都会对应导航器上的属性,因为我们还没有设置我们导航器的任何属性,使用的都是默认的配置,现在我们来为我们的堆栈导航器添加第二个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={()=>}),否则你的组件在被卸载并再次加载的时候回丢失所有的state。

    2.3 设置option参数

    导航器中的每一个路由页面都可以设置一些参数,例如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>
    

    3.1 跳转到一个新页面

    那对于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
            
            onPress={() => navigation.navigate('Details')}
          />
        </View>
      );
    }
    
    // ... other code from the previous section
    

    在上面的代码中,我们用到了navigation和navigate()方法。

    • navigation :在堆栈导航器中,navigation属性会被传递到每一个screen组件中。
    • navigate() : 我们调用navigate方法来实现页面的跳转。

    运行上面的代码,运行效果如下图所示。 React Navigation 5.x详解 如果我们调用navigation.navigate方法跳转到一个我们没有在堆栈导航器中定义的路由的话会怎么呢,什么事情也不会发生。

    3.2 多次跳转到同一个路由

    如果我们多次跳转同一个路由页面会怎么样呢,如下所示。

    function DetailsScreen({ navigation }) {
      return (
        <View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
          <Text>Details Screen</Text>
          <Button
            
            onPress={() => navigation.navigate('Details')}
          />
        </View>
      );
    }
    

    运行上面的代码,当你点击【Go to Details】时,什么事情都不会发生,这是因为我们已经在Details页面了。

    现在我们假设一下,如果是我们想要添加另一个详情页面呢?这是一个很常见的场景,在这个场景中我们想在同一个Details页面展示不同的数据。为了实现这个 ,我们可以用push()方法来替代navigate()方法,push()方法可以直接向堆栈中添加新的路由而无视当前导航历史。

    <Button
      
      onPress={() => navigation.push('Details')}
    />
    

    运行上面的代码,效果如下图所示。 React Navigation 5.x详解

    3.3 路由返回

    堆栈导航器提供的导航头默认包含一个返回按钮,点击按钮可以返回到上一个页面,如果导航堆栈中只有一个页面,也就是说并没有可以返回的页面的时候,这个回退按钮就不显示。

    如果我们使用的是自定义的导航头,可以使用navigation.goBack()方法来实现路由返回,如下所示。

    function DetailsScreen({ navigation }) {
      return (
        <View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
          <Text>Details Screen</Text>
          <Button
            
            onPress={() => navigation.push('Details')}
          />
          <Button  onPress={() => navigation.navigate('Home')} />
          <Button  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
            
            onPress={() => navigation.push('Details')}
          />
          <Button  onPress={() => navigation.navigate('Home')} />
          <Button  onPress={() => navigation.goBack()} />
          <Button
            
            onPress={() => navigation.popToTop()}
          />
        </View>
      );
    }
    

    四、路由传参

    4.1 基本用法

    在页面跳转过程中,免不了会进行数据的传递。在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
            
            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
            
            onPress={() =>
              navigation.push('Details', {
                itemId: Math.floor(Math.random() * 100),
              })
            }
          />
          <Button  onPress={() => navigation.navigate('Home')} />
          <Button  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>
      );
    }
    
    

    React Navigation 5.x详解

    4.2 更新参数

    在路由页面上也可以更新这些参数,navigation.setParams()方法就是用来更新这些参数的。当然,我们也可以设置所传参数的默认值,当跳转页面时没有指定这些参数的值,那么他们就会使用默认值。参数的默认值可以在 initialParams属性中指定,如下所示。

    <Stack.Screen
      name="Details"
      component={DetailsScreen}
      initialParams={{ itemId: 42 }}/>
    

    4.3 返回参数给上一个路由

    我们将将数据通过参数的方式传递给一个新的路由页面,也可以将数据回传给先前路由的页面。例如,有一个路由页面,该页面有一个创建帖子的按钮,这个按钮可以打开一个新的页面来创建帖子,在创建完帖子之后,需要想将帖子的一些数据回传给先前的页面。

    对于这种需求,我们可以使用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
            
            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
            
            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将会被更新并刷新文本。

    4.4 参数传递给嵌套导航器

    如果涉及有嵌套导航器的场景,则需要以稍微不同的方式传递参数。例如,假设在Acount页面嵌套了一个设置页面,并且希望将参数传递到该导航器中的设置页面,那么可以使用下面的方式。

    navigation.navigate('Account', {
      screen: 'Settings',
      params: { user: 'jane' },
    });
    

    五、导航栏配置

    5.1 设置导航栏标题

    React Navigation的Screen组件有一个options属性,这个属性包含了很多可配置的选项,例如设置导航栏的标题,如下所示。

    function StackScreen() {
      return (
        <Stack.Navigator>
          <Stack.Screen
            name="Home"
            component={HomeScreen}
            options={{ title: 'My home' }}
          />
        </Stack.Navigator>
      );
    }
    

    5.2 在标题中使用参数

    为了在标题中使用参数,我们可以把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。

    • navigation:路由屏幕组件的navigation属性
    • route:屏幕组件的route属性

    5.3 使用setOptions更新options

    有时候,我们需要在一个已加载的屏幕组件上更新它的options配置,对于这样的需求,我们可以使用navigation.setOptions来实现。

    /* Inside of render() of React class */
    <Button
      
      onPress={() => navigation.setOptions({ title: 'Updated!' })}/>
    

    5.4 设置导航栏的样式

    React Navigation支持自定义导航头的样式,我们可以使用如下三个样式属性 headerStyle、headerTintColor和headerTitleStyle,含义如下:

    • headerStyle:用于设置包裹住导航头的视图样式,比如为导航栏设置背景色。
    • headerTintColor:回退按钮和title都是使用这个属性来设置他们的颜色。
    • 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 React Navigation 5.x详解 自定义导航栏样式的时候,有以下几点需要注意:

    • 在IOS上,状态栏文本和图标是黑色的,所以你将背景色设置为暗色的话可能看起来不是很好。
    • 以上的设置仅仅只在home页面有效,当我们跳转到details页面的时候,依然会显示默认的颜色。

    5.5 与其他屏幕组件共享options参数

    有时候,我们需要多个页面设置相同的导航头样式。那么,我们可以使用堆栈导航器的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的页面都将使用相同样式的导航头。

    5.6 自定义的组件来替换title属性

    有时候,我们想对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>
      );
    }
    

    六、 导航栏按钮

    6.1 为导航栏添加按钮

    通常,我们可以使用导航头的按钮来实现路由操作。比如,我们点击导航栏左边的按钮可以返回上一个路由,点击导航头的右边可以进行其他操作,如下所示。

    function StackScreen() {
      return (
        <Stack.Navigator>
          <Stack.Screen
            name="Home"
            component={HomeScreen}
            options={{
              headerTitle: props => <LogoTitle {...props} />,
              headerRight: () => (
                <Button
                  onPress={() => alert('This is a button!')}
                  
                  color="#fff"
                />
              ),
            }}
          />
        </Stack.Navigator>
      );
    }
    

    6.2 导航头与屏幕组件的交互

    我们只需要使用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)}  />
          ),
        });
      }, [navigation]);
    
      return <Text>Count: {count}</Text>;
    }
    

    6.3 自定义返回按钮

    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组件也是堆栈导航器中的一个导航页,从而形成了一个导航嵌套的场景。

    导航器的嵌套其实跟我们通常使用的组件的嵌套类似,一般情况下,我们通常会嵌套多个导航器。

    7.1 如何实现导航嵌套

    实现导航器嵌套时,有以下几点需要特别注意:

    1,每个导航器管理自己的导航栈

    比如,当你在一个被嵌套的堆栈导航器上点击返回按钮的时候,它会返回到本导航器(就是被嵌套的堆栈导航器)导航历史中的上一页,而不是返回到上级导航器中。

    2,导航器的一些特定方法在子导航器中同样可用

    在Drawer导航器中嵌套了一个Stack 导航器,那么Drewer导航器的openDrawer、closeDrawer方法在被嵌套的stack导航器的navigation属性中依然是可用的。但是如果Stack导航器没有嵌套在Drawer导航器中,那么这些方法是不可访问的。

    同样,如果你在Stack导航器中嵌套了一个Tab导航器,那么Tab导航器页面中的navigation属性会新得到push和replace这两个方法。

    3, 被嵌套的导航器不会响应父级导航器的事件

    如果Stack导航器被嵌套在tTab导航器中,那么Stack导航器的页面不会响应由父Tab导航器触发的事件,比如我们使用navigation.addListener绑定的tabPress事件。为了能够响应父级导航器的事件,我们可以使用navigation.dangerouslyGetParent().addListener来监听父级导航器的事件。

    4,父级导航器先于子导航器被渲染

    对于上面的这句话,我们怎么理解呢?在Drawer导航器中嵌套了一个Stack导航器,你会看到抽屉的效果先被渲染出来,接着才是渲染Stack导航器的头部,但是如果将Drawer导航器嵌套在Stack导航器中,那么则会先渲染Stack导航器的头部再渲染抽屉效果。因此,我们可以根据这一现象来确认我们需要选择哪种导航器的嵌套。

    7.2 嵌套路由的跳转

    我们先来看一下下面这段代码。

    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' },
    });
    

    7.3 嵌套多个导航栏

    当嵌套多个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的用法不尽相同,且移动端导航更为复杂。

    8.1 Example

    假设有一个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的导航状态已经被保存。

    8.2 React Navigation生命周期

    我们知道,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实现挂钩。

    九、打开一个Modal 全面屏页面

    在RN中,Model是一个弹窗组件,它不是导航中的页面,因此其显示与隐藏都有其独特的方式,我们可以使用它展示一些特别的提示信息,如下图所示。 React Navigation 5.x详解

    9.1 创建一个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')}
            
          />
        </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()}  />
        </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>
      );
    }
    

    首先,我们来看一下上面示例中导航器的示意图。 React Navigation 5.x详解 在上面的示例中,我们使用MainStackScreen作为RootStackScreen的屏幕组件,就相当于我们在一个堆栈导航器中嵌入了另一个堆栈导航器。因此,当我们运行项目时,RootStackScreen会渲染一个堆栈导航器,该导航器有自己的头部,当然我们也可以将这个头部隐藏。

    堆栈导航器的model属性值可以是card(默认)或者model。在IOS上,model的展现方式是从底部滑入,退出的时候则是从上往下的方式来关闭它。在Android上,model是无效的属性,因为全面屏的model与android的平台自带的行为没有任何区别。

    Model堆栈导航器在React Navigation导航中是非常有用的,如果你想变更堆栈导航器页面的过渡动画,那么就可以使用mode属性。当将其设置为modal时,所有屏幕转换之间的过渡动画就会变为从底部滑到顶部,而不是原先的从右侧滑入。但是,需要注意的是,React Navigation的modal会对整个堆栈导航器起效,所以为了在其他屏幕上使用从右侧滑入的效果,我们可以再另外添加一个导航器,这个导航器使用默认的配置就行。

    十 、名称解释

    10.1 Navigator

    Navigator是一个React组件,它决定应用以哪种方式展现已定义的页面。NavigationContainer则是一个管理导航树并包含导航状态的组件,该组件是所有导航器组件的父容器,包装了所有导航器的结构。通常,我们需要将NavigationContainer组件放在应用程序的根目录下,作为启动的根文件。

    function App() {
      return (
        <NavigationContainer>
          <Stack.Navigator> // <---- This is a Navigator
            <Stack.Screen name="Home" component={HomeScreen} />
          </Stack.Navigator>
        </NavigationContainer>
      );
    }
    

    10.2 Router

    Router即是路由器,它是一组函数,它决定如何在导航器中处理用户的操作和状态的更改,通常,开发者不需要直接参与路由器交互,除非您需要编写自定义导航器。

    每个路由都是一个对象,其中包含标识它的键和指定路由类型的“名称”,并且还可以包含任意类型的参数。

    {
      key: 'B',
      name: 'Profile',
      params: { id: '123' }
    }
    

    10.3 Screen component

    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
            
            onPress={() => navigation.navigate('Details')}
          />
          <DetailsScreen />
        </View>
      );
    }
    

    10.4 Navigation Prop

    通过使用Navigation提供的属性,我们可以在两个页面之间传递数据,常用的属性如下。

    • dispatch: 向路由器发送一个动作。
    • navigate, goBack:打开或者返回页面。

    10.5 Navigation State

    导航器的状态如下所示。

    {
      key: 'StackRouterRoot',
      index: 1,
      routes: [
        { key: 'A', name: 'Home' },
        { key: 'B', name: 'Profile' },
      ]
    }
    

    起源地下载网 » React Navigation 5.x详解

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元