• react-navigation 6.x学习(2)


    标题栏按钮

    在头部添加按钮

    与header交互最常见的方式是点击标题左边或右边的按钮。让我们在头部的右侧添加一个按钮(这是整个屏幕上最难触摸的地方,取决于手指和手机的大小,但也是放置按钮的正常位置)。

    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>
      );
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    当我们这样定义按钮时,options中的this变量不是HomeScreen实例,所以你不能调用setState或它的任何实例方法。但是,想让标题中的按钮与标题所属的屏幕交互是非常常见的。那么,我们接下来看看如何做到这一点。

    header与组件的交互

    想要和屏幕组件交互,需要使用navigation.setOptions来定义按钮。在组件中使用navigation.setOptions,我们可以访问屏幕的props,state,conext等。

    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>;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    自定义返回按钮

    createNativeStackNavigator为后退按钮在特定平台上提供默认值。在iOS上,这包括 在按钮旁边的一个标签,当标题适合可用空间时,它会显示上一个屏幕的标题,否则它会显示“后退”。

    你可以用headerBackTitle改变标签行为,用headerBackTitleStyle设置样式。

    要定制后退按钮图片,可以使用headerBackImageSource

    嵌套navigators

    嵌套导航就是在一个Screen中再渲染另一个Screen

    例如

    function Home() {
      return (
        
          
          
        
      );
    }
    
    function App() {
      return (
        
          
            { headerShown: false }}
            />
            
            
          
        
      );
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    在上面的例子中,在Home组件包含一个Tab 导航,而Home组件本身在App组件的stack导航中,所以一个tab导航被嵌套在stack导航中。

    Stack.Navigator

    • Home(Tab.Navigator)
      • Feed (Screen)
      • Messages (Screen)
    • Profile (Screen)
    • Settings (Screen)

    嵌套导航器的工作原理很像嵌套常规组件。为了实现您想要的行为,通常需要嵌套多个导航器。

    嵌套导航需要注意

    1.每个导航器都保留自己的导航历史

    例如,当您在嵌套堆栈导航器的屏幕内按下后退按钮时,它将返回嵌套堆栈中的前一个屏幕,即使有另一个导航器作为父导航器。

    2.每个导航都有自己的options

    例如,在子导航器嵌套的屏幕中指定标题 options,不会影响父导航器中显示的标题。

    3.导航器(navigator)中的每个屏幕(Screen)都有自己的参数

    例如,在嵌套导航器中传递给屏幕的任何参数都在该屏幕的路由支柱中,不能从父或子导航器的屏幕访问。

    如果需要从子屏幕访问父屏幕的参数,可以使用React Context向子屏幕公开参数。

    4.导航操作由当前导航器处理,如果不能处理,就会弹出

    5.导航器特定的方法在嵌套的导航器中可用

    6.嵌套导航器不接收父级事件

    7.父导航器的UI呈现在子导航器之上

    在嵌套导航器中导航到屏幕

    例子

    function Root() {
      return (
        
          
          
          
        
      );
    }
    
    function App() {
      return (
        
          
            { headerShown: false }}
            />
            
          
        
      );
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    从Feed组件跳转到Root组件

    navigation.navigate('Root');
    
    • 1

    它跳转了,并显示了Root组件内的初始屏幕,即Home。但有时您可能想要控制导航时应该显示的屏幕。为了实现它,你可以在参数中传递屏幕的名称:

    navigation.navigate('Root', { screen: 'Profile' });
    
    • 1

    现在,跳转到的是Profile组件而不是Home组件。

    在嵌套导航器中向屏幕传递参数

    可以使用params属性传递参数:

    navigation.navigate('Root', {
      screen: 'Profile',
      params: { user: 'jane' },
    });
    
    • 1
    • 2
    • 3
    • 4

    深层嵌套:

    navigation.navigate('Root', {
      screen: 'Settings',
      params: {
        screen: 'Sound',
        params: {
          screen: 'Media',
        },
      },
    });
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    呈现导航器中定义的初始路由

    如果你需要渲染导航器中指定的初始路由,你可以通过设置initial: false来禁用

    navigation.navigate('Root', {
      screen: 'Settings',
      initial: false,
    });
    
    • 1
    • 2
    • 3
    • 4

    嵌套多个navigators

    有时嵌套多个导航器(如堆栈、抽屉或标签)是很有用的。

    当嵌套多个堆栈、抽屉或底部选项卡导航器时,子导航器和父导航器的标题都会显示出来。然而,通常更可取的做法是在子导航器中显示标题,而在父导航器的屏幕中隐藏标题。

    为了实现这一点,您可以使用headerShown: false选项在包含导航器的屏幕中隐藏标题。

    function Home() {
      return (
        
          
          
        
      );
    }
    
    function App() {
      return (
        
          
            { headerShown: false }}
            />
            
          
        
      );
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    如果你不想在任何导航器中出现头信息,你可以在所有导航器中指定headerShown: false:

    function Home() {
      return (
        { headerShown: false }}>
          
          
        
      );
    }
    
    function App() {
      return (
        
          { headerShown: false }}>
            
            
          
        
      );
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    我们建议将嵌套导航器减少到最少。尽量用最少的嵌套来实现您想要的行为。嵌套有很多缺点:

    它导致深度嵌套的视图层次结构,这可能会导致低端设备的内存和性能问题

    嵌套相同类型的导航器(例如标签嵌在标签中,抽屉嵌在抽屉中等)可能会导致用户体验混乱

    由于过度嵌套,在导航到嵌套屏幕、配置深度链接等时,代码变得难以遵循。

    将嵌套导航器看作是实现您想要的UI的一种方法,而不是组织代码的一种方法。如果您想为组织创建单独的一组屏幕,而不是使用单独的导航器,您可以使用group组件。

    
      {isLoggedIn ? (
        // Screens for logged in users
        
          
          
        
      ) : (
        // Auth screens
        { headerShown: false }}>
          
          
        
      )}
      {/* Common modal screens */}
      { presentation: 'modal' }}>
        
        
      
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    导航的生命周期

    如果你是在一个web后台使用反应式导航,你可以假设当用户从路由a导航到路由B时,a会卸载(它的组件willunmount被调用),当用户返回时,a会再次挂载。虽然这些React生命周期方法仍然有效,并用于反应式导航,但它们的用法不同于web。这是由更复杂的移动导航需求驱动的。

    与 React 相关联的生命周期

    假设在 stack navigator 中有,页面A,页面B。

    • 进入页面A,调用 A.componentDidMount
    • 进入页面B,调用 B.componentDidMount
    • 离开页面B,调用 B.componentWillUnmount
    • 离开页面A,调用 A.componentWillUnmount

    实例场景

    考虑一个带有屏幕a和b的堆栈导航器。在导航到a之后,它的componentDidMount被调用。当push B时,它的componentDidMount也被调用,但A仍然挂载在堆栈上,因此它的componentWillUnmount不被调用。

    当从B返回到A时,B的componentWillUnmount被调用,但A的componentDidMount不被调用,因为A一直处于挂载状态。

    function App() {
      return (
        
          
            
              {() => (
                
                  
                  
                
              )}
            
            
              {() => (
                
                  
                  
                
              )}
            
          
        
      );
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    我们从Home页面导航到Details页面,然后我们使用Tab切换到Settings,在2跳转到Profile。完成这一系列操作后,4个Sceen都被挂载。如果你使用标签栏切换回Home,你会注意到你会出现DetailsScreen,HomeStack的导航状态被保留了!

    导航生命周期事件

    现在,让我们来回答我们在开始时提出的问题:“我们如何发现用户是离开(模糊)它还是回到(聚焦)它?”

    React Navigation向订阅事件的屏幕组件发出事件。我们可以通过监听focus和blur事件来知道屏幕分别何时聚焦或失焦。

    function Profile({ navigation }) {
      React.useEffect(() => {
        const unsubscribe = navigation.addListener('focus', () => {
          // Screen was focused
          // Do something
        });
    
        return unsubscribe;
      }, [navigation]);
    
      return ;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    我们可以使用useFocusEffect钩子来执行副作用,不用手动添加事件监听器。它类似于React的useEffect钩子,但它与导航生命周期相关联。

    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 ;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    如果你想根据屏幕是否聚焦来呈现不同的东西,你可以使用useIsFocused钩子,它返回一个布尔值来指示屏幕是否聚焦。

    总结

    1.虽然React的生命周期方法仍然有效,但React Navigation添加了更多的事件,您可以通过navigation prop订阅这些事件。

    2.你也可以使用useFocusEffect或useIsFocused钩子。

    术语表

    Navigator

    Navigator是React组件,它决定如何呈现已定义的屏幕。它包含Screen元素作为子元素,用于定义屏幕的配置。

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

    function App() {
      return (
        
           // <---- This is a Navigator
            
          
        
      );
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    Router

    路由器是一组函数的集合,它们决定如何处理导航器中的操作和状态变化(类似于Redux应用程序中的reducer)。通常情况下,您永远不需要与路由器直接交互,除非您正在编写一个自定义导航器。

    Screen component

    屏幕组件是我们在路由配置中使用的组件。

    const Stack = createNativeStackNavigator();
    
    const StackNavigator = (
      
        
        
      
    );
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    组件名称中的Screen后缀完全是可选的,但这是一种常用的约定;我们可以叫它迈克尔,这也是一样的。

    Navigation Prop

    这个属性将被传递给所有Screen,它可以用于以下用途:

    1.Dispatch将向路由器发送一个动作

    2.navigate, goBack等可以方便地调度动作

    3.导航器也可以接受导航道具,如果有的话,它们应该从父导航器中获得。

    Route Prop

    这个属性会传送到所有屏幕。包含当前路由的信息,如params, key and name

    Navigation State

    {
      key: 'StackRouterRoot',
      index: 1,
      routes: [
        { key: 'A', name: 'Home' },
        { key: 'B', name: 'Profile' },
      ]
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    对于这种导航状态,有两个路由(可能是选项卡,也可能是堆栈中的卡片)。索引表示活动路由,为B。

    Route

    每个路由都是一个对象,它包含一个用来标识它的键和一个用来指定路由类型的“名称”。它也可以包含任意参数:

    {
      key: 'B',
      name: 'Profile',
      params: { id: '123' }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    Header

    也就是导航头,导航栏,应用栏,还有很多其他东西。这是屏幕顶部的矩形,包含后退按钮和屏幕标题。整个矩形通常被称为React Navigation中的标题。

  • 相关阅读:
    关于俄罗斯游戏玩家特征的几个事实
    【Effect C++ 笔记】(四)设计与声明
    不同环境下RabbitMQ的安装-2 ARM架构、X86架构、Window系统环境下安装RabbitMQ
    【计算机视觉 | 目标检测】arxiv 计算机视觉关于目标检测的学术速递(8 月 24 日论文合集)
    第二证券:知名私募美股持仓曝光 科技与消费板块成“心头好”
    Spark任务调度概述_大数据培训
    java获取近期视频流关键帧与截图
    【Python数值积分】
    node NPM镜像源查看和切换
    初阶C语言 - 分支语句(if、switch)
  • 原文地址:https://blog.csdn.net/wxl1390/article/details/126246176