React-Native配置@react-navigation/stack@6.x——使用自定义导航栏/通用路由模块/统一切换效果

本篇文章所讲的内容均可以在这个库中查看hao-react-navigation,下载安装依赖即可运行
在这里插入图片描述

相关依赖版本

"@react-native-masked-view/masked-view": "^0.2.6",
"@react-navigation/native": "^6.0.2",
"@react-navigation/stack": "^6.0.7",
"react": "17.0.2",
"react-native": "0.65.1",
"react-native-gesture-handler": "^1.10.3",
"react-native-safe-area-context": "^3.3.2",
"react-native-screens": "^3.7.2",

安装准备

1. 安装react-navigation

npm install @react-navigation/native react-native-screens react-native-safe-area-context @react-navigation/stack react-native-gesture-handler @react-native-masked-view/masked-view

App.js顶部第一行引入

import 'react-native-gesture-handler';

2. 安卓配置

MainActivity.java中添加如下代码

import android.os.Bundle; // 顶部添加

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

配置使用

为了使整个应用都使用统一的页面切换风格,以及便于管理页面路由,我封装了一个路由模块组件,只需要传入页面列表。

1. 路由模块

navigatorConfig.js代码

import React from "react";
import { TransitionPresets } from "@react-navigation/stack";

const getCurrentScreenIndex = ({navigation, route}) => {
  return navigation.getState().routes.findIndex(item => {
    return item.key === route.key;
  });
}

export const NavigatorConfig = {
  StackNavigatorDefaultConfig: {
    screenOptions: ({navigation, route}) => {
      const screenIndex = getCurrentScreenIndex({navigation, route})
      const screenCount = navigation.getState().index;
      // false不从内存中释放页面
      // 配置为堆栈最顶部的两个页面不释放
      const detachPreviousScreen = screenCount - screenIndex > 1;
      return {
        headerShown: false, // 关闭默认导航
        gestureEnabled: true, // 手势可操作
        ...TransitionPresets.SlideFromRightIOS, // 这里使用的是传统的右边滑入
        detachPreviousScreen,
      }
    }
  },
}

routerModule.js部分代码

import { NavigationContainer } from "@react-navigation/native";
import { createStackNavigator } from "@react-navigation/stack";
const Stack = createStackNavigator();
import { NavigatorConfig } from "./navigatorConfig"; // 每个页面通用的配置

<NavigationContainer ref={this.props.navRef}>
  <Stack.Navigator {...NavigatorConfig.StackNavigatorDefaultConfig} {...this.props.config}>
    {
      this.props.page && this.props.page.map((item, index) => {
        item.options = item.options || {};
        item.params = item.params || {};
        const Component = item.view;
        return (
          <Stack.Screen key={item.name}
                        name={item.name}
                        options={item.options}
                        {...item.config}
          >
            {
              props => <Component {...props} {...item.params} title={item.title || undefined}/>
            }
          </Stack.Screen>
        )
      })
    }
  </Stack.Navigator>
</NavigationContainer>

使用路由模块代码示例(App.js)

const IndexRouterConfig = [
    {
        name: 'LoginForm', // 必填,页面名称,用于跳转
        view: LoginForm, // 必填,继承于NavPage的页面组件
        title: '', // 选填,页面的导航标题       
        params: {}, // 选填,给页面组件的props    
        options: {}, // 选填,<Stack.Screen>的options,可配置页面动画效果
        config: {}, // 选填,<Stack.Screen>的整体props,会覆盖上面的取值
    }
]

<RouterModule 
	page={IndexRouterConfig} 
	config={{
      // 可使用这个config覆盖默认的页面配置即NavigatorConfig
      initialRouteName: 'LoginForm'                             
    }}
    navRef={navigationRef} // 第二点讲到
/>

2. 使用导航

  1. 封装navigationRef使得所有页面都可以通过context使用导航方法

    import { createNavigationContainerRef, StackActions, CommonActions } from '@react-navigation/native';
    export const navigationRef = createNavigationContainerRef();
    
    navigationRef.push = (...args) => {
      if (navigationRef.isReady()) {
        navigationRef.dispatch(StackActions.push(...args));
      } else {
        alert('页面还没准备好');
      }
    }
    
    navigationRef.goBack = (...args) => {
      setTimeout(() => {
        navigationRef.dispatch(CommonActions.goBack(...args));
      }, 0)
    }
    
    
  2. App.js中使用context传播到各个页面和组件中(可用新的context方法)

    import { navigationRef} from "./navigation/navigationRef";
    
    static childContextTypes = {
        navigation: PropTypes.object
    };
    getChildContext() {
        return {
            navigation: navigationRef
        };
    }
    
    
    
    

3. 页面组件

封装了一个页面组件NavPage,所有的需要导航的页面都需要继承自这个组件,只能在renderPage中渲染页面内容。

// NavPage.js
import React from "react";
import { TouchableOpacity, View, Text } from "react-native";
import PropTypes from "prop-types";

class NavPage extends React.Component {
  static defaultProps = {
    showBackButton: true
  }

  static contextTypes = {
    navigation: PropTypes.object,
  };

  renderNavigationTitle() {
    return this.props.title || '';
  }

  renderNavigationBar() {
    return (
      <View style={{
        height: 50,
        paddingHorizontal: 10,
        backgroundColor: 'tomato',
        alignItems: 'center',
        flexDirection: "row",
        justifyContent: 'flex-start'
      }}>
        {
          (this.context.navigation && this.context.navigation.isReady() && this.context.navigation.canGoBack()) &&
          !!this.props.showBackButton && <TouchableOpacity onPress={() => {
            this.context.navigation.goBack()
          }}>
            <Text style={{color: 'white', fontSize: 12}}>返回</Text>
          </TouchableOpacity>
        }
        <Text style={{color: 'white', fontSize: 16, marginLeft: 30}}>
          {this.renderNavigationTitle()}
        </Text>
      </View>
    );
  }

  renderPage() {
    return null;
  }

  render() {
    return (
      <View style={{flex: 1}}>
        {this.renderNavigationBar()}
        {this.renderPage()}
      </View>
    )
  }
}

export default NavPage;

业务页面使用

LoginForm.js

import React from "react";
import { Text, TouchableOpacity, View } from "react-native";
import NavPage from "../navigation/navPage";

class LoginForm extends NavPage {
  static defaultProps = {
    ...NavPage.defaultProps,
    title: '登陆页面',
  };

  renderPage() {
    return (
      <View>
        <TouchableOpacity
          onPress={() => {
            this.context.navigation.push('Register')
          }}
        >
          <Text>注册页</Text>
        </TouchableOpacity>
      </View>
    )
  }
}

export default LoginForm;
  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值