今天看到了探花交友的验证码输入完成后,需要根据是否是新用户,跳转不同的页面,但是,可以明显感觉到现有的路由配置不适合页面比较多的场景,扩展性并不好,每次都需要写到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;