关于前端:React-Navigation-5x详解

4次阅读

共计 20918 个字符,预计需要花费 53 分钟才能阅读完成。

一、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,就能够看到提醒了

二、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 文件的门路,并运行我的项目,成果如下图所示。

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={()=><HomeScreen />}), 否则你的组件在被卸载并再次加载的时候回失落所有的 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
        title="Go to Details"
        onPress={() => navigation.navigate('Details')}
      />
    </View>
  );
}

// ... other code from the previous section

在下面的代码中,咱们用到了 navigation 和 navigate()办法。

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

运行下面的代码,运行成果如下图所示。

如果咱们调用 navigation.navigate 办法跳转到一个咱们没有在堆栈导航器中定义的路由的话会怎么呢,什么事件也不会产生。

3.2 屡次跳转到同一个路由

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

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')}
/>

运行下面的代码,成果如下图所示。

3.3 路由返回

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

如果咱们应用的是自定义的导航头,能够应用 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>
  );
}

四、路由传参

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

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
        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 将会被更新并刷新文本。

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
  title="Update the title"
  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

自定义导航栏款式的时候,有以下几点须要留神:

  • 在 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!')}
              title="Info"
              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)} title="Update count" />
      ),
    });
  }, [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 是一个弹窗组件,它不是导航中的页面,因而其显示与暗藏都有其独特的形式,咱们能够应用它展现一些特地的提示信息,如下图所示。

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')}
        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 会对整个堆栈导航器起效,所以为了在其余屏幕上应用从右侧滑入的成果,咱们能够再另外增加一个导航器,这个导航器应用默认的配置就行。

十、名称解释

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
        title="Go to Details"
        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'},
  ]
}
正文完
 0