接触了RN之后,必不可免得要接触界面之间跳转之类的需求,而这一类需求的实现必须要使用到Navigator这个导航器,这次记录一下使用过程中对于Navigator导航器的认知。
首先要理解这个导航器,可以通俗的理解和Android中activity的堆栈管理一样,导航器除了界面导航功能之外,还提供界面栈的管理,界面的跳入和跳出。(RN中每一个component都相当于一个组件,一个或多个component共同构成场景(Scene),场景通俗的理解就是一个占据整个屏幕的界面)
RN中的入口是index.android.js(以Android为例),这个index.js可以看成整个RN组建的框架,一些基础的东西都在这里实例化、定义。我们的导航器也需要在这个文件中被创建。(后文中提到的BackAndroid也在这里面定义)
Navigator属性介绍
Navigator中包含如下属性:
initialRoute
初始化路由,初始化需要显示的Component,其中的component参数必须要有,定义如下:
initialRoute={{title:'main',id:'main',component:defaultComponent}}
configureScene
配置场景动画,系统提供了很多动画,如从底部弹出,从左弹出等,参数如下:
组件中定义:
configureScene={this._configureScence.bind(this)}
_configureScence(route) {
console.log("AndroidTestComponent=====configureScenceAndroid"+ route.type)
if(route.type =='Bottom') {
returnNavigator.SceneConfigs.FloatFromBottom;// 底部弹出
}else if(route.type =='Left') {
returnNavigator.SceneConfigs.FloatFromLeft// 右侧弹出
}else if(route.type =='Right') {
returnNavigator.SceneConfigs.FloatFromRight//左侧弹出
}
returnNavigator.SceneConfigs.PushFromRight;// 默认右侧弹出
}
renderScene
场景渲染,根据路由来确定要挂载渲染的场景,设置如下:
组件中定义
renderScene={this._scene.bind(this)}
//场景渲染方法,传入路由器和导航器两个方法
_scene(route, navigator) {
console.log(route)
//这个里面如果不做处理,默认返回的是initialRoute初始化的component
letComponent= route.component;
//路由器的params可以携带参数
//将改导航器传递给下一个Component
return
//或者直接引入一个现成的Component
//return
}
ref
这个属性有点很微妙,网上很多介绍Navigator的博客代码中没有写这个属性,这个属性相当于给一个组件添加一个label标签,然后通过该标签可以找到对应的组件,发现这个属性的原因是我在写BackAndroid的时候,需要使用到navigator这个对象,在监听物理返回键的时候判断是否还有路由存在,通常在方法中获取navigator的方法如下:
_pressButton(){
const {navigator} = this.props;
}
这么写的前提是_pressButton该方法一般都会被bind,而且该Component在挂载前已经把navigator传递过来了,所以可以获得到,但是我们在index.js中使用BackAndroid,定义方法不管是使用箭头函数或者在构造方法中bind对应的方法,这个时候this.props都没有navigator这个属性,所以这个时候是找不到的,也就没办法实现导航回退的功能,而使用ref就很好的解决这个问题了,即子组件获取父组件通过props、父组件获取子组件通过refs。如下设置:
在组建中添加:
ref="navigator"
方法中调用:
onBackAndroid=()=>{
constnavigator=this.refs.navigator;
...
}
Navigator方法
getCurrentRoutes() - 获取当前栈里的路由列表,也就是push进来,没有pop掉的那些
jumpBack() - 跳回之前的路由,保留现在的,还可以再跳回来。相当于浏览器的回退
jumpForward() - 结合jumpBack,此方法再重新打开回退前的,相当于浏览器的前进
jumpTo(route) - 跳转到一个没有被取消挂载的已存在场景
push(route) - push一个新的路由场景
pop() - 移除并取消挂载当前的场景,回到上一个场景
replace(route) -用一个新的路由场景替代当前的场景,该方法之后当前的场景就被取消挂载了
replaceAtIndex(route,index) -通过制定index下标replace
replacePrevious(route) -replace前一个场景
immediatelyResetRouteStack(routeStack) -用新的路由场景Stack重置堆栈中的每一个场景
popToRoute(route) - 移除并取消挂载当前场景到制定场景之间的对
popToTop() - 移除并取消挂载出堆栈中第一个场景外的其他场景
其中route路由最基本的就是
var route = {component: LoginComponent}
完整代码如下
//component是从react中来的
importReact, {Component}from'react';
//Text以及View等都是从react-native中来的
import{
AppRegistry,
StyleSheet,
Navigator,
BackAndroid,
Dimensions
}from'react-native';
importsplashfrom'./app/mainview/splash'
importguidefrom'./app/mainview/guide'
//定义一个Component,按照ES6的语法来,就和java语法中定义class一样,继承component
export default classAndroidTestComponentextendsComponent{
//构造函数
constructor(props) {
super(props)
//如果_onBackAndroid不是一个箭头函数,需要在构造函数中bind this,要不然在添加监听和移除监听时操作的对象是不同的
// this._onBackAndroid = this.onBackAndroid.bind(this)
}
//场景动画
_configureScence(route) {
console.log("AndroidTestComponent=====configureScenceAndroid"+ route.type)
if(route.type =='Bottom') {
returnNavigator.SceneConfigs.FloatFromBottom;// 底部弹出
}else if(route.type =='Left') {
returnNavigator.SceneConfigs.FloatFromLeft// 右侧弹出
}else if(route.type =='Right') {
returnNavigator.SceneConfigs.FloatFromRight//左侧弹出
}
returnNavigator.SceneConfigs.PushFromRight;// 默认右侧弹出
}
//场景渲染
_scene(route, navigator) {
letComponent= route.component;
//传递参数以及导航器
return
}
//使用箭头函数,直接绑定this,不需要再构造函数中再去bind
onBackAndroid=()=>{
//使用refs来获取导航器
constnavigator=this.refs.navigator;
if(!navigator){
return false;
}
constrouters=navigator.getCurrentRoutes();
if(routers.length>1){
navigator.pop();
return true;
}else{
return false;
}
}
//compoment将要挂载的函数,这个时候可以在继续更新state 添加监听
componentWillMount() {
console.log("AndroidTestComponent=====componentWillMount")
BackAndroid.addEventListener('hardwareBackPress',this.onBackAndroid)
}
//render属性对应的函数会返回一段JSX来表示该组件的结构和布局。该部分是一个组件必不可少的地方,没有这些内容,就无法构成一个组件。
//render方法必须返回单个根元素
//compoment挂载渲染的函数
render() {
//定义默认闪屏界面
letdefaultComponent= splash;
return(
configureScene={this._configureScence.bind(this)}
renderScene={this._scene.bind(this)}
ref="navigator"
/>
);
}
//compoment已经挂载的函数
//界面渲染完之后,在进行一些数据处理,比如网络数据加载,比如本地数据加载
componentDidMount() {
console.log("AndroidTestComponent=====componentDidMount")
}
//作为子控件时,当期属性被改变时调用
componentWillReceiveProps(nextProps) {
console.log("AndroidTestComponent=====componentWillReceiveProps")
}
//component将要更新时调用
componentWillUpdate(nextProps, nextState) {
console.log("AndroidTestComponent=====componentWillUpdate")
}
//component更新后调用
componentDidUpdate(prevProps, prevState) {
console.log("AndroidTestComponent=====componentDidUpdate")
}
//component销毁时调用
componentWillUnmount() {
console.log("AndroidTestComponent=====componentWillUnmount")
BackAndroid.removeEventListener('hardwareBackPress',this.onBackAndroid)
}
}
conststyles=StyleSheet.create({
container: {
flex:1,
justifyContent:'flex-start',
alignItems:'stretch',
backgroundColor:'white'
},
lineStyle: {
backgroundColor:'grey',
height:0.3,
},
loadText: {
fontSize:20,
textAlign:'center',
margin:10
},
loadView: {
flex:1,
alignItems:'center',
justifyContent:'center'
},
});
//另一种定义props的方法,如果static defaultProps也定义了,这个会覆盖上面的
// AndroidTestComponent.defaultProps = {
// name:'xiaoerlang'
// }
//进行注册 'RNProject'为项目名称 AndroidTestComponent 为启动的component
AppRegistry.registerComponent('RNProject', () => AndroidTestComponent);
过程中遇到的问题及解决方案:
react native - expected acomponent class, got [object Object]
该错误是引用了小写的组件,组件首字母一定要大写,比如<splash/>应该写成<Splash>