react-native6.x路由的使用

本文章目前使用的是最新版的React-Native0.72.7
Node.js >= 16
JDK = 11

// package.json
"dependencies": {
   "@react-navigation/native": "^6.1.9",
   "@react-navigation/native-stack": "^6.9.17",
   "react": "18.2.0",
   "react-native": "0.72.7",
   "react-native-safe-area-context": "^4.7.4",
   "react-native-screens": "^3.27.0"
}

安装 react-navigation/native

yarn add @react-navigation/native

安装 react-native-screens和react-native-safe-area-context

yarn add react-native-screens react-native-safe-area-context

react-native-screens需要额外的配置步骤才能在Android设备上正常工作,找到项目下android/app/src/main/java/<your package name>/MainActivity.java
将下面的代码添加到MainActivity类的主体中

public class MainActivity extends ReactActivity {
  // ...
  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(null);
  }
  // ...
}

在MainActivity.java文件顶部引入

import android.os.Bundle;

我们需要将整个应用程序封装在NavigationContainer中。通常你会在你的入口文件中这样做,比如index.js或App.js

// App.tsx
import { NavigationContainer } from '@react-navigation/native'

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

在典型的React Native应用程序中,NavigationContainer应该只在根应用程序中使用一次。除非有特定的用例,否则不应该嵌套多个NavigationContainers。

安装@react-navigation/native-stack

Native Stack 使用 Android 和 IOS 原生导航系统在页面之间导航

yarn add @react-navigation/native-stack

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

NavigationContainer是一个管理导航树并包含导航状态的组件。此组件必须包装所有导航器结构。通常,我们会在应用程序的根目录下呈现这个组件,通常是从app.js导出的组件。

// App.tsx
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()

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

@react-navigation/native-stack需要依赖我们刚刚装过的那些库

在这里插入图片描述

以上图片是运行后的界面
导航栏和内容区域的样式是堆栈导航器stack navigator的默认配置
路由名称的大小写无关紧要——您可以使用小写的home或大写的home,这取决于您自己。我们更喜欢将路线名称大写。

首屏渲染 initialRouteName

让我们在本机堆栈导航器中添加第二个屏幕,并将主屏幕配置为首先渲染

// App.tsx

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

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组件接受一个与我们将用于导航的路线名称相对应的名称prop,以及一个与它将渲染的组件相对应的组件prop。
使用initialRouteName属性设置首屏渲染
这里,Home路由对应于HomeScreen组件,Details路由对应于DetailsScreen组件。堆栈的初始路由是Home路由。尝试将其更改为Details并重新加载应用程序

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

React Native的快速刷新不会像您所期望的那样更新initialRouteName中的更改,请注意,您现在将看到Details屏幕。然后将其更改回“主页”并再次重新加载。

Stack.Screen的options属性

导航器中的每个屏幕都可以为导航器指定一些选项,例如要在标题中呈现的标题。这些选项可以在每个屏幕组件的选项道具中传递

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

在这里插入图片描述
以上图片蓝色圈中的文字就是options中传递的title

Stack.Screen自定义传递数据

const someData = [{ name: 'zhangs' }, { name: '李四' }]

const HomeScreen = ({ navigation, extraData }) => {    
    return (
      <View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
        <Text>我是Home Screen</Text>
        <Text>我是extraData{JSON.stringify(extraData)}</Text>
      </View>
    )
  }

<NavigationContainer>
  <Stack.Navigator initialRouteName='Home'>
    <Stack.Screen name='Home' options={{ title: '我是Home的title' }}>
        {/* extraData自定义数据 */}
        {props => <HomeScreen {...props} extraData={someData} />}
        </Stack.Screen>
    <Stack.Screen name='Details' component={DetailsScreen} />
  </Stack.Navigator>
</NavigationContainer>

在这里插入图片描述

以上蓝色圈中是传递的数据

Stack.Navigator 是一个组件,它将路由配置作为其子级,并为配置提供额外的道具,并呈现我们的内容。

Stack.Screen组件采用一个name的prop和component prop
name prop引用路由的名称
component prop指定要为路由渲染的组件
这是两个必需的prop

navigation.push 有历史记录的跳转

<Button
  title="我是详情页的按钮"
  onPress={() => navigation.push('Details')}
/>

如果在Details里面使用 navigation.navigate(‘Details’) 的话他不会在继续跳转,因为已经在当前页面了
在Details里面使用 navigation.push(‘Details’) 这种写法就可以在Details里面继续跳转详情页

navigation.goBack 返回

从Home页面里面跳转到Details里面返回的时候可用 navigation.goBack方法返回

// App.tsx
const DetailsScreen = ({ navigation }) => (
   <View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
     <Text>我是Details Screen</Text>
     <Button title='返回goBack()' onPress={() => navigation.goBack()} />
   </View>
)

如果已经在第一个页面了,而且堆栈里面也没有其他路由的话使用此方法会报错(这只是一个仅用于开发的警告,不会在生产中显示。)
错误提示如下
在这里插入图片描述

navigation.popToTop 回到第一个页面

如果您在一个堆栈中有几个屏幕,并且希望忽略所有屏幕返回第一个屏幕。在这种情况下,我们知道我们想回到主页,这样我们就可以使用navigation.navigate(‘Home’),这里还有一个方法可以在堆栈中的屏幕直接返回第一个屏幕,使用 navigation.popToTop方法即可

// App.tsx
const DetailsScreen = ({ navigation }) => (
   <View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
     <Text>我是Details Screen</Text>
     <Button title='回到第一个页面' onPress={() => navigation.popToTop()} />
   </View>
)

params 路由传参

navigation.push(跳转的路径, 携带的参数),navigation.navigate亦是如此
在Detais1里面接收参数的时候,先在参数里面结构出来route,route里面有一个params对象,params对象里面就是Home1里面带过来的参数,建议传递的params是JSON可序列化的

// App.tsx
import { View, Text, Button } from 'react-native'
import { NavigationContainer } from '@react-navigation/native'
import { createNativeStackNavigator } from '@react-navigation/native-stack'

const Stack = createNativeStackNavigator()

export default function App() {

  const someData = [{ name: 'zhangs' }, { name: '李四' }]

  const HomeScreen1 = ({ navigation }) => (
    <View>
      <Text>我是HomeScreen1</Text>
      <Button title='params带参数传参' onPress={() => navigation.push('Details1', someData)} />
    </View>
  )

  const DetailsScreen1 = ({ route }) => {
    const params = route.params || {}
    return (
      <View>
        <Text>我是DetailsScreen1</Text>
        <Text>我是接收到的参数:{JSON.stringify(params)}</Text>
      </View>
    )
  }

  return (
    <NavigationContainer>
      <Stack.Navigator initialRouteName='Home'>
        <Stack.Screen name='Home1' component={HomeScreen1} />
        <Stack.Screen name='Details1' component={DetailsScreen1} />
      </Stack.Navigator>
    </NavigationContainer>
  )
}

initialParams 初始参数

您可以将一些初始参数传递到screen。如果导航到此screen时没有指定任何参数,则将使用初始参数。它们也与您传递的任何参数浅合并。可以使用initialParams指定初始参数

// App.tsx
import { View, Text, Button } from 'react-native'
import { NavigationContainer } from '@react-navigation/native'
import { createNativeStackNavigator } from '@react-navigation/native-stack'

const Stack = createNativeStackNavigator()

export default function App() {

  const HomeScreen1 = ({ navigation }) => (
    <View>
      <Text>我是HomeScreen1</Text>
      <Button title='不带参数跳转' onPress={() => navigation.push('Details1')} />
    </View>
  )

  const DetailsScreen1 = ({ route }) => {
    const params = route.params
    return (
      <View>
        <Text>我是DetailsScreen1</Text>
        <Text>我是默认参数:{JSON.stringify(params)}</Text>
      </View>
    )
  }

  return (
    <NavigationContainer>
      <Stack.Navigator initialRouteName='Home'>
        <Stack.Screen name='Home1' component={HomeScreen1} />
        <Stack.Screen name='Details1' initialParams={[{name: '小明'}]} component={DetailsScreen1} />
      </Stack.Navigator>
    </NavigationContainer>
  )
}

navigation.setParams 修改路由传递的参数

Screens也可以更新参数,就像跟新state一样,navigation.setParams方法用于更新Screens的参数

// App.tsx
import { View, Text, Button } from 'react-native'
import { NavigationContainer } from '@react-navigation/native'
import { createNativeStackNavigator } from '@react-navigation/native-stack'

const Stack = createNativeStackNavigator()

export default function App() {

  const HomeScreen1 = ({ navigation }) => (
    <View>
      <Text>我是HomeScreen1</Text>
      <Button title='不带参数跳转' onPress={() => navigation.push('Details1')} />
    </View>
  )

  const DetailsScreen1 = ({ route }) => {
    const params = route.params
    return (
      <View>
        <Text>我是DetailsScreen1</Text>
        <Text>我是默认参数:{JSON.stringify(params)}</Text>
        <Button title='修改params参数' onPress={() => navigation.setParams({ name: '小明修改' })} />
      </View>
    )
  }

  return (
    <NavigationContainer>
      <Stack.Navigator initialRouteName='Home'>
        <Stack.Screen name='Home1' component={HomeScreen1} />
        <Stack.Screen name='Details1' initialParams={[{name: '小明'}]} component={DetailsScreen1} />
      </Stack.Navigator>
    </NavigationContainer>
  )
}

避免使用setParams更新标题等屏幕选项。如果需要更新选项,请使用setOptions。(这是官方的注释)

将 params 传递到上个 screen

参数不仅对将某些数据传递到新screen有用,而且对将数据传递到前一个screen也有用。
为了实现这一点,您可以使用navigate方法,如果 screen已经存在,该方法的作用类似于goBack。 您可以通过navigation传递params以将数据传递回

import { useState, useEffect } from 'react'
import { View, Text, Button, TextInput } from 'react-native'
import { NavigationContainer } from '@react-navigation/native'
import { createNativeStackNavigator } from '@react-navigation/native-stack'

const Stack = createNativeStackNavigator()

export default function App() {

  const HomeScreen = ({ navigation, route }) => {

    useEffect(() => {
      // 每次这里都会发生变化
      console.log(route.params)
    }, [route.params?.postText])

    return (
      <View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
        <Text>我是Home Screen</Text>
        <Text>我是postText:{route.params?.postText}</Text>
        <Button title='跳转详情' onPress={() => navigation.navigate('Details')} />
      </View>
    )
  }

  const DetailsScreen = ({ route, navigation }) => {

    const [postText, setPostText] = useState()

    return (
      <View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
        <Text>我是Details Screen</Text>
        <TextInput
          placeholder='请输入'
          style={{ height: 50, width: '90%', padding: 10, backgroundColor: 'white' }}
          value={postText}
          onChangeText={setPostText}
        />
        <Button
          title='返回携带参数'
          onPress={() => {
            navigation.navigate({
              name: 'Home',
              params: { postText },
              merge: true
            })
          }}
        />
      </View>
    )
  }

  return (
    <NavigationContainer>
      <Stack.Navigator initialRouteName='Home'>
        <Stack.Screen name='Home' component={HomeScreen} />
        <Stack.Screen name='Details' initialParams={{ name: '小明', age: 18 }} component={DetailsScreen} />
      </Stack.Navigator>
    </NavigationContainer>
  )
}

merge 表示合并参数,如果不设置该参数或者为false,则原来的参数会被替换,而不是合并,请注意!
例如:

  1. About页面跳转到Home页面的时候路由的params里面携带了一个name字段
  2. 在Home页面中使用route.params.name显示出来
  3. 在从Home页面跳转到Details页面
  4. 在Details页面里面再次跳转到上个Home页面的时候路由params携带了一个postText字段
    4.1 此时merge字段为空或者设置为false的时候,再次回到Home页面第一次从About页面携带的name字段就没了,因为已经Details页面携带的字段替换掉了
    4.2 如果设置merage字段为true的话那么再次回到Home页面的时候那个从About页面携带的name字段就不会被替换,而是合并
    合并后的params就是 params: {name: ‘我是从About页面携带的参数’, postText: ‘我是从Details页面携带的参数’}

params的变更可以通过监听 navigation的state变化获取

在标题中使用params(options作为函数的使用)

options可以写成一个函数,如果我们将选项作为一个函数,那么React Navigation将用一个包含{Navigation,route}的对象来调用它

// App.tsx
import { useState} from 'react'
import { View, Text, Button, TextInput } from 'react-native'
import { NavigationContainer } from '@react-navigation/native'
import { createNativeStackNavigator } from '@react-navigation/native-stack'

const Stack = createNativeStackNavigator()

export default function App() {

  const HomeScreen = ({ navigation, route }) => 
    return (
      <View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
        <Text>我是Home Screen</Text>
        <Text>我是postText:{route.params?.postText}</Text>
        <Button title='跳转详情' onPress={() => navigation.navigate('Details')} />
      </View>
    )
  }
  const DetailsScreen = ({ route, navigation }) => {
  
    const [postText, setPostText] = useState()
    
    return (
      <View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
        <Text>我是Details Screen</Text>
        <TextInput
          placeholder='请输入'
          style={{ height: 50, width: '90%', padding: 10, backgroundColor: 'white' }}
          value={postText}
          onChangeText={setPostText}
        />
        <Button
          title='返回携带参数'
          onPress={() => {
            navigation.navigate({
              name: 'Home',
              params: { postText },
              merge: true
            })
          }}
        />
      </View>
    )
  }

  return (
    <NavigationContainer>
      <Stack.Navigator initialRouteName='Home'>
      	// 注意这里的options是一个箭头函数
        <Stack.Screen name='Home' component={HomeScreen} options={({route}) => ({title: route.params.postText})} />
        <Stack.Screen name='Details' initialParams={{ name: '小明', age: 18 }} component={DetailsScreen} />
      </Stack.Navigator>
    </NavigationContainer>
  )
}

在这里插入图片描述

  1. 上图中Home页面默认的title就是Home
  2. 跳转到Details页面中在输入框写入文字,并且携带参数跳转到Home页面
  3. 在Stack.Screen中设置了options并且为函数,在其中把route.params中的postText赋值给了title
  4. 再次到Home页面的时候看头部就是路由里面携带的postText字段了

传递给options函数的参数是一个具有以下属性的对象:
navigation-屏幕的导航道具。
route-屏幕的路由道具
在这里我们只需要上面例子中的route prop,但在某些情况下,您可能也想使用navigation。

setOptions 更新选项(header styles)

在自定义页眉样式时,有三个关键属性可供使用:headerStyle、headerIntColor和headerTitleStyle。

// App.tsx
import { View, Text, Button } from 'react-native'
import { NavigationContainer } from '@react-navigation/native'
import { createNativeStackNavigator } from '@react-navigation/native-stack'

const Stack = createNativeStackNavigator()

const HomeScreen = ({ navigation }) => (
  <View>
    <Text>HomeScreen</Text>
  </View>
)
export default function App() {
  return (
    <NavigationContainer>
      <Stack.Navigator initialRouteName='Home'>
        <Stack.Screen
          name='Home'
          component={HomeScreen}
          options={{
            headerStyle: {
              backgroundColor: '#f4511e'
            },
            headerTintColor: '#fff',
            headerTitleStyle: {
              fontWeight: 'bold'
            }
          }}
        />
      </Stack.Navigator>
    </NavigationContainer>
  )
}

在这里插入图片描述

headerStyle:将应用于包装页眉的视图的样式对象。如果你在上面设置backgroundColor,那将是你的标题的颜色。
headerIntColor:后退按钮和标题都使用此属性作为颜色。在下面的示例中,我们将色调设置为白色(#fff),因此后退按钮和标题将为白色。
headerTitleStyle:如果我们想自定义标题的fontFamily、fontWeight和其他Text样式属性,我们可以使用它。

screenOptions(跨屏幕共享常用选项)

上面的例子中您会注意到,当您导航到DetailsScreen时,颜色会返回到默认值。如果我们必须将选项标题样式的属性从HomeScreen复制到DetailsScreen,以及我们在应用程序中使用的每个屏幕组件,那不是很糟糕吗?

我们可以将配置写在Stack.Navigator screenOptions中。

// App.tsx
import { View, Text, Button } from 'react-native'
import { NavigationContainer } from '@react-navigation/native'
import { createNativeStackNavigator } from '@react-navigation/native-stack'

const Stack = createNativeStackNavigator()

const HomeScreen = ({ navigation }) => (
  <View>
    <Text>HomeScreen</Text>
    <Button title='跳转到详情页' onPress={() => navigation.navigate('Details')} />
  </View>
)
const DetailsScreen = () => (
  <View>
    <Text>DetailsScreen</Text>
  </View>
)

export default function App() {
  return (
    <NavigationContainer>
      <Stack.Navigator
        initialRouteName='Home'
        screenOptions={{
          headerStyle: {
            backgroundColor: '#f4511e'
          },
          headerTintColor: '#fff',
          headerTitleStyle: {
            fontWeight: 'bold'
          }
        }}
      >
        <Stack.Screen name='Home' component={HomeScreen} />
        <Stack.Screen name='Details' component={DetailsScreen} />
      </Stack.Navigator>
    </NavigationContainer>
  )
}

在这里插入图片描述

现在我们所有的screen都是这种配置的颜色了

Stack.Screen custom component(自定义组件)

有时,您需要更多的控制权,而不仅仅是更改标题的文本和样式——例如,您可能需要渲染一个图像来代替标题,或者将标题制作成一个按钮。在这些情况下,您可以完全覆盖用于标题的组件,并提供自己的组件。

// App.tsx
import { View, Text, Button, Image } from 'react-native'
import { NavigationContainer } from '@react-navigation/native'
import { createNativeStackNavigator } from '@react-navigation/native-stack'

const Stack = createNativeStackNavigator()

// 使用自定义组件替代title
const LogoTitle = () => (
  <Image
    style={{ width: 50, height: 50 }}
    source={{ uri: 'https://ts1.cn.mm.bing.net/th?id=OIP-C.Zte3ljd4g6kqrWWyg-8fhAHaEo&w=316&h=197&c=8&rs=1&qlt=90&o=6&pid=3.1&rm=2' }}
  />
)

const HomeScreen = ({ navigation }) => (
  <View>
    <Text>HomeScreen</Text>
    <Button title='跳转到详情页' onPress={() => navigation.navigate('Details')} />
    
  </View>
)
const DetailsScreen = () => (
  <View>
    <Text>DetailsScreen</Text>
  </View>
)

export default function App() {
  return (
    <NavigationContainer>
      <Stack.Navigator initialRouteName='Home' screenOptions={{ headerTitle: props => <LogoTitle {...props} /> }}>
        <Stack.Screen name='Home' component={HomeScreen} />
        <Stack.Screen name='Details' component={DetailsScreen} />
      </Stack.Navigator>
    </NavigationContainer>
  )
}

在这里插入图片描述

当我们提供组件而不是标题时,为什么要像以前那样使用headerTitle?原因是headerTitle是一个特定于页眉的属性,而title也将用于选项卡栏、抽屉等。
headerTitle默认为显示标题的Text组件。
options可以是一个对象或一个函数。当它是一个函数时,它会被提供一个带有navigation和route的对象。

此链接可查看native stack navigator全部options完整列表

options中的headerRight

与标题交互的最常见方式是点击标题左侧或右侧的按钮。让我们在标题的右侧添加一个按钮

<Stack.Navigator>
  <Stack.Screen
    name="Home"
    component={HomeScreen}
    options={{
      headerRight: () => (
        <Button
          onPress={() => alert('This is a button!')}
          title="Info"
          color="#fff"
        />
      )
    }}
  />
</Stack.Navigator>

header与其screen component的交互

在某些情况下,标头中的组件需要与屏幕组件交互。对于这个用例,我们需要使用navigation.setOptions来更新我们的选项。通过在屏幕组件中使用navigation.setOptions,我们可以访问屏幕的props, state, context等

import { useEffect, useState } from 'react'
import { View, Text, Button, Image } from 'react-native'
import { NavigationContainer } from '@react-navigation/native'
import { createNativeStackNavigator } from '@react-navigation/native-stack'

const Stack = createNativeStackNavigator()

const HomeScreen = ({ navigation }) => {
  const [count, setCount] = useState(0)

  useEffect(() => {
    navigation.setOptions({
      headerRight: () => <Button title='更新值' onPress={() => setCount(c => c + 1)} />
    })
  }, [navigation])

  return <Text>count:{count}</Text>
}

export default function App() {
  return (
    <NavigationContainer>
      <Stack.Navigator initialRouteName='Home'>
        <Stack.Screen name='Home' component={HomeScreen} />
      </Stack.Navigator>
    </NavigationContainer>
  )
}

在这里插入图片描述

嵌套导航(Nesting navigators)

嵌套导航器意味着在另一个导航器的屏幕内呈现导航器,也就是二级路由
待定。。。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值