React Native使用@react-navigation/native配置页面路由

今天看到了探花交友的验证码输入完成后,需要根据是否是新用户,跳转不同的页面,但是,可以明显感觉到现有的路由配置不适合页面比较多的场景,扩展性并不好,每次都需要写到nav.js文件中,如果页面特别多的话,这个文件会特别大,多人合作的话,冲突也会比较多。

 参考Rn使用@react-navigation/native配置页面路由以及导航栏_51CTO博客_react native导航栏对现有的路由进行改造。

一、新建路由项目结构

 index.js存放我们的全部页面,StackNavigator.js存放我们目前的堆栈页面,TabNavigator.js存放我们的底部tab栏,这里我们没用到,暂时放这里。

二、代码改造

1、index.js存放我们的全部页面

import Login from '../pages/account/login';
import Demo from '../pages/Demo/Demo';

const router = [
  //   {
  //     name: 'Index', //跳转路径
  //     title: '明知山', //头部展示标题
  //     component: require('./TabNavigator').default,
  //   },
  //下面只需要配置非tabbar页面路径
  {
    name: 'Demo',
    title: 'Demo',
    component: Demo,
  },
  {
    name: 'Login',
    title: '登录',
    component: Login,
  },
];
export default router;

2、stackNavigator使用@react-navigation/native-stack配置路由

import React from 'react';
import {createNativeStackNavigator} from '@react-navigation/native-stack';
import {getFocusedRouteNameFromRoute} from '@react-navigation/native';
import router from './index';
import {isShowDemo} from '../constant/Limits';

const Stack = createNativeStackNavigator();

const StackNavigator = () => {
  //从子导航器获取路由名称
  const getChildTitle = route => {
    const routeName = getFocusedRouteNameFromRoute(route);
    return routeName;
  };
  return (
    <Stack.Navigator initialRouteName={isShowDemo ? 'Demo' : 'Login'}>
      {router.map((item, index) => {
        return (
          <Stack.Screen
            key={index}
            name={item.name}
            component={item.component}
            options={({route}) => ({
              title: getChildTitle(route) || item.title,
              headerStyle: {
                backgroundColor: '#fff',
                height: 40,
              },
              headerTitleStyle: {
                color: '#000',
                fontSize: 15,
              },
              headerShown: false, //不显示头部标题
            })}
          />
        );
      })}
    </Stack.Navigator>
  );
};

export default StackNavigator;

3、nav.js配置全局路由

// In App.js in a new project

import React from 'react';
import {NavigationContainer} from '@react-navigation/native';
import StackNavigator from './router/StackNavigator';

const Nav = () => {
  return (
    <NavigationContainer>
      <StackNavigator />
    </NavigationContainer>
  );
};

export default Nav;

三、适配IOS的刘海屏

尽管RN提供了SafeAreaView,但它有一些问题,React Navigation提供了更好用的react-native-safe-area-context。

1、首先在根组件使用SafeAreaProvider,这是一个提供者,本身不会对布局产生影响,只有在该组件包裹下的子组件才能使用react-native-safe-area-context提供的功能,因此我们通常把它包裹在App组件。

// In App.js in a new project

import * as React from 'react';
import Nav from './src/nav';
import {SafeAreaProvider} from 'react-native-safe-area-context';

function App() {
  return (
    <SafeAreaProvider>
      <Nav></Nav>
    </SafeAreaProvider>
  );
}

export default App;

2、我们要适配的页面引入SafeAreaView自动处理

import React, {useState} from 'react';
import {View, Image, StyleSheet, StatusBar, Text} from 'react-native';
import {styles} from '../../../constant/Styles';
import {Images} from '../../../constant/Images';
import {pxToDp} from '../../../utils/stylesKits';
import {Input} from '@rneui/themed';
import validator from '../../../utils/validator';
import {ACCOUNT_LOGIN} from '../../../utils/pathMap';
import request from '../../../utils/request';
import LoginBtn from '../../../components/LoginBtn';
import {CodeField, Cursor} from 'react-native-confirmation-code-field';
import {SafeAreaView} from 'react-native-safe-area-context';

const Index = () => {
  // 电话号码
  const [phoneNumber, setPhoneNumber] = useState('');
  // 电话号码校验是否通过
  const [isValid, setValid] = useState(true);
  // 是显示“手机号登录注册”页面,还是显示“验证码输入”页面
  const [isShowLogin, setShowLogin] = useState(true);
  // 验证码
  const [vCodeValue, setVCodeValue] = useState('');
  // 重新获取验证码btn显示文字
  const [btnTxt, setBtnTxt] = useState('重新获取');
  // 重新获取--防重
  const [isDisabled, setDisable] = useState(false);

  phoneNumberSubmitEditing = async () => {
    console.log('手机号码输入完毕!', validator.validatePhone(phoneNumber));
    //   校验手机号码
    //   校验不通过
    if (!validator.validatePhone(phoneNumber)) {
      setValid(false);
      return;
    }
    //   校验通过
    setValid(true);
    // const res = await request.post(ACCOUNT_LOGIN, {phone: phoneNumber});
    setShowLogin(false);
    countDown();
  };

  // 手机号登录
  loginView = () => {
    return (
      <View>
        <Text style={styles.Title}>手机号登录注册</Text>
        <View style={Styles.inputContainer}>
          <Input
            placeholder="请输入手机号码"
            leftIcon={{
              type: 'font-awesome',
              name: 'phone',
              color: '#ccc',
              size: pxToDp(20),
            }}
            maxLength={11}
            keyboardType="phone-pad"
            inputStyle={{color: '#333'}}
            errorMessage={isValid ? '' : '手机号码格式不正确'}
            value={phoneNumber}
            onChangeText={setPhoneNumber}
            onSubmitEditing={phoneNumberSubmitEditing}
          />
        </View>
        <View style={Styles.btnContainer}>
          <LoginBtn onPress={phoneNumberSubmitEditing}>获取验证码</LoginBtn>
        </View>
      </View>
    );
  };
  // 输入验证码
  vCodeView = () => {
    return (
      <View>
        <Text style={styles.Title}>输入六位验证码</Text>
        <View style={Styles.sepTitle}>
          <Text style={styles.Title}>已经发到:+86 {phoneNumber}</Text>
        </View>
        <CodeField
          // Use `caretHidden={false}` when users can't paste a text value, because context menu doesn't appear
          value={vCodeValue}
          onChangeText={setVCodeValue}
          cellCount={6}
          rootStyle={Styles.codeFieldRoot}
          keyboardType="number-pad"
          renderCell={({index, symbol, isFocused}) => (
            <Text
              key={index}
              style={[Styles.cell, isFocused && Styles.focusCell]}>
              {symbol || (isFocused ? <Cursor /> : null)}
            </Text>
          )}
        />
        <View style={[Styles.btnContainer, Styles.btnMargin]}>
          <LoginBtn disabled={isDisabled} onPress={countDown}>
            {btnTxt}
          </LoginBtn>
        </View>
      </View>
    );
  };
  // 重新获取验证码倒计时
  countDown = () => {
    if (isDisabled) {
      return;
    }
    setDisable(true);
    let seconds = 5;
    setBtnTxt(`重新获取(${seconds}s)`);
    let timeId = setInterval(() => {
      seconds--;
      setBtnTxt(`重新获取(${seconds}s)`);
      if (seconds === 0) {
        clearInterval(timeId);
        setBtnTxt('重新获取');
        setDisable(false);
      }
    }, 1000);
  };

  return (
    <SafeAreaView style={styles.Container}>
      {/* 状态栏 */}
      <StatusBar backgroundColor={'transparent'} translucent={true} />
      {/* 背景图片 */}
      <Image source={Images.loginBG} style={Styles.imgSize} />
      {/* 登录信息 */}
      <View style={Styles.loginContainer}>
        {isShowLogin ? loginView() : vCodeView()}
      </View>
    </SafeAreaView>
  );
};

const Styles = StyleSheet.create({
  imgSize: {
    width: '100%',
    height: pxToDp(200),
  },
  loginContainer: {
    padding: pxToDp(20),
  },
  inputContainer: {
    marginTop: pxToDp(30),
  },
  btnContainer: {
    width: '85%',
    height: pxToDp(40),
    alignSelf: 'center',
  },
  sepTitle: {
    marginTop: pxToDp(15),
  },
  root: {
    flex: 1,
    padding: pxToDp(20),
  },
  codeFieldRoot: {
    marginTop: 20,
  },
  cell: {
    width: pxToDp(40),
    height: pxToDp(40),
    lineHeight: pxToDp(38),
    fontSize: pxToDp(24),
    borderBottomWidth: pxToDp(2),
    borderColor: '#7d53ea',
    textAlign: 'center',
    color: '#7d53ea',
  },
  focusCell: {
    borderColor: '#7d53ea',
  },
  btnMargin: {
    marginTop: pxToDp(60),
  },
});

export default Index;

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值