react-navigation 6.x学习

react-navigation介绍

作用:

使用react-navigation来实现各个界面的跳转和不同板块的切换。

react-navigation主要包括三个组件:

  • StackNavigator 导航组件
  • TabNavigator 切换组件
  • DrawerNavigator 抽屉组件

StackNavigator -用于实现各个页面之间的跳转: 一次只渲染一个页面,并提供页面之间跳转的方法。 当打开一个新的页面时,它被放置在堆栈的顶部

TabNavigator - 渲染一个选项卡,让用户可以在几个页面之间切换

DrawerNavigator - 提供一个从屏幕左侧滑入的抽屉效果

安装

yarn add @react-navigation/native
或
npm install @react-navigation/native

安装依赖关系
在项目的路径下,执行下面的依赖命令

yarn add react-native-reanimated react-native-gesture-handler react-native-screens react-native-safe-area-context @react-native-community/masked-view

如果是mac环境

npx pod-install ios

react-native-screens包需要一个额外的配置步骤才能在Android设备上正常工作。编辑MainActivity.java文件,它位于android/app/src/main/java/<你的包名>/MainActivity.java

MainActivity 文件里面添加下面代码:

@Override
protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(null);
}

并确保在文件的顶部添加一个import语句:

import android.os.Bundle;

NavigationContainer 包装应用程序

把整个应用包装在NavigationContainer中。通常会在你的入口文件中这样做,比如index.js或App.js:

import * as React from 'react';
import { NavigationContainer } from '@react-navigation/native';

export default function App() {
  return (
    <NavigationContainer>{/* Rest of your app code */}</NavigationContainer>
  );
}

在一个典型的React Native应用中,NavigationContainer应该只在应用的根目录中使用一次,不应该嵌套多个navigationcontainer。

react-navigation/stack介绍

在web浏览器中,您可以使用锚(<a>标签)链接到不同的页面。当用户单击链接时,URL被推送到浏览器历史堆栈。当用户按下后退按钮时,浏览器会从历史堆栈的顶部弹出该项,因此活动页面现在是以前访问过的页面。React Native不像web浏览器那样有一个内置的全局历史堆栈的想法——这是React Navigation进入故事的地方。

react-navigation的stack(堆栈导航器)提供了在屏幕之间切换和管理导航历史的方法。当用户在应用程序上交互时,应用程序从堆栈中推送和弹出项目,可以使用户看到不通的屏幕。与web浏览器的区别,它提供了在Android和ios中在栈中路由之间导航时所期望的手势和动画。

我们上面安装的库是导航器的构建块和共享基础,React Navigation中的每个导航器都有自己的库。要使用堆栈导航器,我们需要安装@react-navigation/native-stack:

安装navigator库

npm install @react-navigation/native-stack
yarn add @react-navigation/native-stack

创建导航器

createNativeStackNavigator是一个函数,它返回一个包含两个属性的对象:Screen和Navigator。它们都是用于配置导航器的React组件。Navigator应该包含Screen元素作为其子元素,以定义路由的配置。

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 { createNativeStackNavigator } from '@react-navigation/native-stack';

function HomeScreen() {
  return (
    <View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
      <Text>Home Screen</Text>
    </View>
  );
}

const Stack = createNativeStackNavigator();

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

export default App;

如果运行此代码,您将看到一个屏幕,其中有一个空的导航栏和一个包含您的home屏幕组件的灰色内容区域(如上所示)。您看到的导航栏和内容区域的样式是堆栈导航器的默认配置,我们稍后将学习如何配置它们。

路由名称的大小写并不重要——你可以使用小写的home或大写的home,这取决于你。我们更喜欢将路由名称大写。

Stack.Screen必须配置的有名称和组件。

配置导航器

例子

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

const Stack = createNativeStackNavigator();

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组件。堆栈的初始路由是Home路由。尝试将其更改为Details并重新加载应用程序(React Native的快速刷新不会从initialRouteName更新更改),注意,你现在会看到详细信息屏幕。然后将其更改为Home并再次重新加载。

Screen 的component属性接受的是组件,而不是函数。不要传递内联函数(例如component={() => <HomeScreen />}),否则当父组件重新渲染时,你的组件将卸载并重新加载,失去所有状态。

options

stack中的每个Screen组件都可以为导航器指定一些选项,例如在头部中呈现的标题。这些属性可以被传递到每个Screen组件的options属性中:

例子

<Stack.Screen
  name="Home"
  component={HomeScreen}
  options={{ title: 'Overview' }}
/>

有时,我们希望为导航器中的所有屏幕指定相同的选项。为此,我们可以向导航器传递一个screenOptions属性。

添加多个props

有时我们可能想要将很多属性传递给屏幕。我们可以通过两种方法做到这一点:

1.使用React context并使用上下文提供程序包装导航器 来将数据传递给屏幕(推荐)。

2.使用回调函数,传递多个props

例子

<Stack.Screen name="Home">
  {(props) => <HomeScreen {...props} extraData={someData} />}
</Stack.Screen>

总结:

1.React Native 不能像web浏览器那样有一个内置的导航API。React Navigation提供了这个功能,以及实现了IOS和Android的手势和动画来在屏幕之间切换。

2.Stack.Navigator是一个组件,路由配置为它的子组件,所有路由常用的配置选项可以用screenOptions属性配置

3.每个Stack.Screen组件都必须有一个name属性,一个component属性指定路由。

4.指定堆栈的初始路由,需要使用initialRouteName

屏幕切换

在上一节,我们定义了两个路由Home和Details,但是还没有学习如何从Home到Details.

如果在Web浏览器中,我们会这样写:

<a href="details.html">Go to Details</a>

或者下面的写法:

<a
  onClick={() => {
    window.location.href = 'details.html';
  }}
>
  Go to Details
</a>

我们将执行与后者类似的操作,但不是使用window.location。

导航到新屏幕

例子

import * as React from 'react';
import { Button, View, Text } from 'react-native';
import { NavigationContainer } from '@react-navigation/native';
import { createNativeStackNavigator } from '@react-navigation/native-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 - navigation属性将被传递给堆栈中的每一个组件;

navigate('Details') - 用户要移动到的路由

多次导航到一个路由

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

如果已经在当前路由上,再次跳转到这个路由,将什么都不会做。

有时我们需要跳转到另一个Detail屏幕,需要向每个路由传递一些唯一的数据。我们可以用push更改导航。

<Button
  title="Go to Details... again"
  onPress={() => navigation.push('Details')}
/>

每次调用push时,我们都会向导航堆栈添加一个新路由。当你调用navigate时,它首先会尝试找到一个具有该名称的现有路由,并且只有在堆栈中还没有路由的情况下才会推送一个新的路由。

回到上一个路由

有时我们需要回到上一个页面,需要使用 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>
  );
}

在Android上,React Navigation与硬件后退按钮挂钩,并在用户按下后退按钮时为你触发goBack()函数,因此它的行为与用户期望的一样。

另一个常见的需求是能够返回多个屏幕 — 有多个屏幕,你希望返回到第一个屏幕,可以使用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>
  );
}

总结:

1.navigation.navigate(‘RouteName’)如果路由还未在堆栈中,则将其推送到本机堆栈导航器,否则将跳转到该屏幕。

2.我们可以任意调用navigation.push(‘RouteName’),它会继续推送路由。

3.标题栏将自动显示一个后退按钮,但是您可以通过调用navigation.goBack()以编程的方式返回。在安卓系统上,硬件返回按钮的工作原理和预期一样。

4.你可以使用navigation.navigate(‘RouteName’)返回堆栈中的一个现有屏幕,也可以使用navigation.popToTop()返回堆栈中的第一个屏幕。

5.navigation属性可用于所有屏幕组件(在路由配置中定义为Screen的组件,并由React navigation作为路由呈现)。

向路由传递参数

现在我们已经知道了如何创建带有一些路由的堆栈导航器,并在这些路由之间导航,让我们看看在跳转路由时,如何将数据传递给下一个组件。

这里有两个部分:

1.通过将参数放入一个对象中,对象作为navigation.navigate的第二个参数

navigation.navigate('RouteName', { /* params go here */ })

2.在跳转到的Screen组件中读取params获得参数 :route.params

我们建议您传递的参数是json序列化的。

例子

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

更新参数

Screen也可以更新他们的参数,使用navigation.setParams方法

navigation.setParams({
  query: 'someText',
});

避免使用setParams来更新屏幕选项,如标题等。如果需要更新选项,请使用setOptions。

初始参数

你可以向Screen传递一些初始参数。如果导航到此屏幕时没有指定任何参数,则将使用初始参数。它们还与您传递的任何参数浅层合并。初始参数可以用initialParams属性来指定:

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

向上一个屏幕传递参数

参数不仅向新屏幕传递一些数据有用,而且向前一个屏幕传递数据也有用。例如,假设您有一个带有“创建发布”按钮的屏幕,“创建发布”按钮打开一个新屏幕来创建一个帖子。创建之后,您希望将提交的数据传递回上一个屏幕。

为此,可以使用navigate方法,如果屏幕已经存在,它的作用类似于goBack。你可以通过导航传递参数来传递回数据:

例子

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 and merge params back to home screen
          navigation.navigate({
            name: 'Home',
            params: { post: postText },
            merge: true,
          });
        }}
      />
    </>
  );
}

在这里,你按“Done”后,home页面的参数将被更新。

向嵌套导航器传递参数

如果有嵌套的导航器,传递参数的方式就有点不同了。例如,假设您在一个导航器有一个Account页面,希望将参数传递Settings页面。然后你可以像下面这样传递参数:

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

哪些参数在params中

params就像屏幕的选项,他们应该只包含屏幕显示内容的信息。应该避免传递屏幕的完成数据(例如,一个页面显示用户信息,应该传递用户id而不是用户对象)。还要避免传递多个屏幕使用的数据,这样的数据应该放在全局存储中。

// Don't do this
navigation.navigate('Profile', {
  user: {
    id: 'jane',
    firstName: 'Jane',
    lastName: 'Done',
    age: 25,
  },
});

这看起来很方便,并且允许您使用route.params.user访问用户对象,而无需任何额外的工作。

然而,这是一种反模式。像用户对象这样的数据应该在全局存储中,而不是在导航状态中。否则,您将在多个地方复制相同的数据。这可能会导致错误,例如,即使用户对象在导航后发生了更改,配置文件屏幕也会显示过时的数据。

可能会出现以下问题:

1.URL是屏幕的表示,所以它也需要包含参数,即完整的用户对象,这可能会使URL非常长和不可读

2.因为用户对象在URL中,所以可能会传递一个随机的用户对象,表示一个不存在的用户,或者配置文件中有不正确的数据

3.如果没有传递用户对象,或者格式化不正确,这可能会导致崩溃,因为屏幕不知道如何处理它

更好的方法是只在参数中传递用户的ID:

navigation.navigate('Profile', { userId: 'jane' });

现在,可以使用传递的userId从全局存储中获取用户。这消除了许多问题,比如过时的数据或有问题的url。

一些应该在params中的例子:

1.id,如用户id,项目id等,例如navigation.navigate('Profile', { userId: 'Jane' })

2.当你有一个项目列表时,用于排序、过滤数据等的参数,例如navigation.navigate('Feeds', { sortBy: 'latest' })

3.用于分页的时间戳、页码或游标,例如 navigation.navigate('Chat', { beforeTime: 1603897152675 })

4.在屏幕上输入的数据,例如,navigation.navigate('ComposeTweet', { title: 'Hello world!' })

本质上,就是传递屏幕所需的最少的参数。

总结

1.navigate和push接受可选的第二个参数,让你把数据传递给要跳转到的路由。例如,``navigation.navigate(‘RouteName’, { paramName: ‘value’ }).

2.可以通过route.params获取页面的参数

3.可以使用navigation.setParams更新屏幕的参数

4.可以使用initialParams属性在Screen上传递初始参数

5.params应该包含显示屏幕所需的最小数据

配置标题栏

设置头部title

Screen组件接受options属性,它可以是一个对象,也可以是一个返回对象的函数,该对象包含各种配置选项。我们使用的标题是title,如下面的例子所示。

例子

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

在title中使用params

为了在标题中使用参数,需要在Screen的options中返回回调函数,参数有{ navigation, route } ,可以通过route获取params

例子

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

用setOptions更新options

通常需要在当前页面本身,更新本页面的options,我们可以用navigation.setOptions

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

调整header样式

在自定义标题样式时,有三个关键属性可以使用:

headerStyle, headerTintColor, and headerTitleStyle.

headerStyle: 一个样式对象,它将应用于包装header的视图。如果你在上面设置了backgroundColor,那将是你的标题的颜色。

headerTintColor:后退按钮和title都使用这个属性作为它们的颜色。在下面的例子中,我们将颜色设置为白色(#fff),这样后退按钮和头部标题就会是白色。

headerTitleStyle:如果我们想为title定制fontFamily, fontWeight和其他文本样式属性,我们可以使用它来完成。

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

这里有几点需要注意:

1.在iOS上,状态栏文本和图标是黑色的,这在深色背景下看起来不太好。我们不会在这里讨论它,但您应该确保按照状态栏指南中的描述 配置状态栏以适应您的屏幕颜色。

2.我们设置的配置只适用于主屏幕;当我们导航到详细信息屏幕时,默认样式又回来了。现在我们来看看如何在屏幕之间共享选项。

跨页面共享opitons

通常希望在多个屏幕上,以类似的方式配置header。例如,您的公司品牌颜色可能是红色,所以您希望header的背景颜色是红色,而色调颜色是白色。

在上面运行示例中使用的颜色在Home上,当你跳转到DetailsScreen时,颜色会回到默认值。如果我们必须将选项头样式应用到每个组件,我们可以将配置移到Stack.Navigator的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>
  );
}

现在,任何屏幕,都将有我们精彩的品牌风格。当然,如果我们需要的话,一定有一种方法可以覆盖这些选项。

用自定义组件替换标题

有时,您需要更多的控制,而不仅仅是更改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>
  );
}

你可能想知道,为什么我们提供了一个headerTitle而不是title,就像以前一样?原因是headerTitle是一个特定于堆栈导航器的属性,headerTitle默认为一个显示标题的Text组件。

总结

1.您可以在Screen组件的options中自定义header。

2.options属性可以是对象或函数。当它是一个函数时,它被提供一个带有导航和路由参数的对象。

3.您还可以在初始化堆栈导航器配置时,指定公共的screenOptions属性。props优先于该配置。

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值