RN学习笔记之Navigator

接触了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>

推荐阅读更多精彩内容