react-navigation 6.x学习
react-navigation
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)