随着React Navigation逐渐稳定,Navigator也被光荣的退休了。在React Native生态环境中需要一款可扩展且易于使用的导航组件,Navigator 自然胜任不了,这时React Native社区便孕育出了一个开源导航组件React Navigation。
React Navigation的出现替代了Navigator、 Ex-Navigation等老一代的导航组件,React Navigation可以说是Navigator的加强版,不仅有Navigator的全部功能,另外还支持底部导航类似于与iOS中的UITabBarController,此外它也支持侧拉效果方式的导航类似于Android中的抽屉效果。
这篇文章将向大家分享React Navigation3x开发的一些实用技巧,以及从navigator到React Navigation的一些实战经验。
导航器也可以看成一个是普通的React组件,你可以通过导航器来定义你的App的导航结构。 导航器还可以渲染通用元素,例如可以配置的标题栏和选项卡栏。
在React Navigation中有以下7种类型的导航器:
- createStackNavigator: 类似于普通的Navigator,屏幕上方导航栏;
- createTabNavigator: createTabNavigator已弃用,使用createBottomTabNavigator和/或createMaterialTopTabNavigator替代;
- createBottomTabNavigator:相当于iOS里面的TabBarController,屏幕下方的标签栏;
- createMaterialTopTabNavigator:屏幕顶部的材料设计主题标签栏;
- createDrawerNavigator: 抽屉效果,侧边滑出;
- createSwitchNavigator:SwitchNavigator 的用途是一次只显示一个页面。
你可以通过以上7种导航器来创建你APP,可以是其中一个也可以多个组合,这个可以根据具体的应用场景并结合每一个导航器的特性进行选择。
在开始学习7种导航器之前,我们需要先了解两个和导航关于概念:
Screen navigation prop(屏幕导航属性)
:通过navigation可以完成屏幕之间的调度操作,例如打开另一个屏幕;Screen navigationOptions(屏幕导航选项)
: 通过navigationOptions可以定制导航器显示屏幕的方式(例如:头部标题,选项卡标签等);
const SomeNav = createStackNavigator/createBottomTabNavigator/createMaterialTopTabNavigator/createDrawerNavigator/createSwitchNavigator({
// config
});
<SomeNav
screenProps={xxx}
ref={nav => { navigation = nav; }}
onNavigationStateChange=(prevState, newState, action)=>{
}
/>
- ref:可以通过
ref
属性获取到navigation
; - onNavigationStateChange(prevState, newState, action):顶级节点除了
ref
属性之外,还接受onNavigationStateChange(prevState, newState, action)
属性,每次当导航器所管理的state
发生改变时,都会回调该方法;- prevState:变化之前的state;
- newState:新的state;
- 导致state变化的action;
- screenProps:向子屏幕传递额外的数据,子屏幕可以通过this.props.screenProps获取到该数据。
当导航器中的屏幕被打开时,它会收到一个navigation
prop,navigation
prop是整个导航环节的关键一员,接下来就详细讲解一下navigation
的作用。
- navigate:跳转到其他界面;
- state:屏幕的当前state;
- setParams:改变路由的params;
- goBack:关闭当前屏幕;
- dispatch:向路由发送一个action;
- addListener:订阅导航生命周期的更新;
- isFocused:true 标识屏幕获取了焦点;
- getParam:获取具有回退的特定参数;
- dangerouslyGetParent:返回父导航器;
注意:一个navigation有可能没有navigate、setParams以及goBack,只有state与dispatch,所以在使用navigate时要进行判断,如果没有navigate可以使用navigation去dispatch一个新的action。如:
const {navigation,theme,selectedTab}=this.props;
const resetAction = StackActions.reset({
index: 0,
actions: [
NavigationActions.navigate({
routeName: 'HomePage',
params:{
theme:theme,
selectedTab:selectedTab
},
})
]
})
navigation.dispatch(resetAction)
提示:这里的
reset
在2.0及以后版本中被从NavigationActions中移到了StackActions
中,使用时记得留意。
StackNavigator的navigation的额外功能:
当且仅当当前 navigator 是 stack navigator 时,this.props.navigation
上有一些附加功能。 这些函数是 navigate 和 goBack 的替代方法, 你可以使用任何你喜欢的方法。 这些功能是:
- this.props.navigation
- push - 导航到堆栈中的一个新的路由
- pop - 返回堆栈中的上一个页面
- popToTop - 跳转到堆栈中最顶层的页面
- replace - 用新路由替换当前路由
- reset - 擦除导航器状态并将其替换为多个操作的结果
- dismiss - 关闭当前栈
navigation.navigate({routeName, params, action, key})
或navigation.navigate(routeName, params, action)
- routeName:要跳转到的界面的路由名,也就是在导航其中配置的路由名;
- params:要传递给下一个界面的参数;
- action:如果该界面是一个navigator的话,将运行这个sub-action;
- key:要导航到的路由的可选标识符。 如果已存在,将后退到此路由;
export const AppStackNavigator = createStackNavigator({
HomeScreen: {
screen: HomeScreen
},
Page1: {
screen: Page1
})
class HomeScreen extends React.Component {
render() {
const {navigate} = this.props.navigation;
return (
<View>
<Text>This is HomeScreen</Text>
<Button
onPress={() => navigate('Page1', {name: 'Devio'})}
title="Go to Page1"
/>
</View>
)
}
}
可以通过this.props.state.params来获取通过setParams()
,或navigation.navigate()
传递的参数。
<Button
title={params.mode === 'edit' ? '保存' : '编辑'}
onPress={() =>
setParams({mode: params.mode === 'edit' ? '' : 'edit'})}
/>
<Button
title="Go To Page1"
onPress={() => {
navigation.navigate('Page1',{ name: 'Devio' });
}}
/>
const {navigation} = this.props;
const {state, setParams} = navigation;
const {params} = state;
const showText = params.mode === 'edit' ? '正在编辑' : '编辑完成';
setParams: function setParams(params)
:
我们可以借助setParams
来改变route params,比如,通过setParams
来更新页面顶部的标题,返回按钮等;
class ProfileScreen extends React.Component {
render() {
const {setParams} = this.props.navigation;
return (
<Button
onPress={() => setParams({name: 'Lucy'})}
title="Set title name to 'Lucy'"
/>
)
}
}
注意navigation.setParams改变的是当前页面的Params,如果要改变其他页面的Params可以通过NavigationActions.setParams完成,下文会讲到。
在使用React Navigation3x过程中遇到任何问题都可以在React Navigation3x的视频教程中寻找答案哈。
-
goBack: function goBack(key)
:我们可以借助goBack
返回到上一页或者路由栈的指定页面。- 其中
key
表示你要返回到页面的页面标识如id-1517035332238-4
,不是routeName。 - 可以通过指定页面的
navigation.state.key
来获得页面的标识。 - key非必传,也可传null。
navigation.state {params: {…}, key: "id-1517035332238-4", routeName: "Page1"} ```
- 其中
export default class Page1 extends React.Component {
render() {
const {navigation} = this.props;
return <View style={{flex: 1, backgroundColor: "gray",}}>
<Text style={styles.text}>欢迎来到Page1</Text>
<Button
title="Go Back"
onPress={() => {
navigation.goBack();
}}
/>
</View>
}
}
dispatch: function dispatch(action)
:给当前界面设置action,会替换原来的跳转,回退等事件。
const resetAction = StackActions.reset({
index: 0,
actions: [
NavigationActions.navigate({
routeName: 'HomePage',
params:{
theme:theme,
selectedTab:selectedTab
},
})
]
})
navigation.dispatch(resetAction)
NavigationActions
- Navigate : 导航到其他的页面;
- Back : 返回到上一个页面;
- Set Params : 设置指定页面的Params;
- Init : 初始化一个 state 如果 state 是 undefined;
Navigatie action会使用Navigate action的结果来更新当前的state。
方法原型:
navigate({routeName, params, action, key})
- routeName:字符串,必选项,在app的router里注册的导航目的地的routeName。
- params:对象,可选项,融合进目的地route的参数。
- actions:对象,可选项(高级),如果screen也是一个navigator,次级action可以在子router中运行。在文档中描述的任何actions都可以作为次级action。
- key:
string or null
可选,要导航到的路由的标识符。如果已存在, 则导航回此路由。
import { NavigationActions } from 'react-navigation'
const navigateAction = NavigationActions.navigate({
routeName: 'Profile',
params: {},
action: NavigationActions.navigate({ routeName: 'SubProfileRoute'})
})
this.props.navigation.dispatch(navigateAction)
返回到前一个screen并且关闭当前screen.backaction creator接受一个可选的参数:
方法原型:
back(key)
- key:
String
可选,这个可以和上文中讲到的goBack的key是一个概念;
import { NavigationActions } from 'react-navigation'
const backAction = NavigationActions.back();
this.props.navigation.dispatch(backAction);
通过SetParams我们可以修改指定页面的Params。
- params:对象,必选参数,将会被合并到已经存在页面的Params中。
- key:字符串,必选参数,页面的key。
import { NavigationActions } from 'react-navigation'
const setParamsAction = NavigationActions.setParams({
params: { title: 'HomePage' },
key: 'id-1517035332238-4',
});
有很多小伙伴可能会问:navigation中有setParams为什么还要有NavigationActions.setParams?
我从两方面来回答一下这个问题:
- 在上文中讲到过navigation中有可能只有state与dispatch,这个时候如果要修改页面的Params,则只能通过
NavigationActions.setParams
了; - 另外,navigation.setParams只能修改当前页面的Params,而
NavigationActions.setParams
可以修改所有页面的Params;
- Reset : 重置当前 state 到一个新的state;
- Replace : 使用另一个路由替换指定的路由;
- Push : 在堆栈顶部添加一个页面,然后跳转到该页面;
- Pop : 跳转到上一个页面;
- PopToTop : 跳转到堆栈最顶层的页面,并销毁其他所有页面;
Reset action删掉所有的navigation state并且使用这个actions的结果来代替。
- index,数组,必选,navigation state中route数组中激活route的index。
- actions,数组,必选项,Navigation Actions数组,将会替代navigation state。
- key:
string or null
可选, 如果设置,具有给定 key 的导航器将重置。 如果为null,则根导航器将重置。
import { NavigationActions, StackActions } from 'react-navigation'
const resetAction = StackActions.reset({
index: 0,
actions: [
NavigationActions.navigate({ routeName: 'Profile'})
]
})
this.props.navigation.dispatch(resetAction)
使用场景比如进入APP首页后的splash页不在使用,这时可以使用
NavigationActions.reset
重置它。
index参数被用来定制化当前激活的route。举个例子:使用两个routes WelcomePage和HomePage给一个基础的stack navigation设置。为了重置route到HomePage,但是在堆栈中又存放在WelcomePage之上,你可以这么做:
import { NavigationActions, StackActions } from 'react-navigation'
const resetAction = StackActions.reset({
index: 1,
actions: [
NavigationActions.navigate({ routeName: 'WelcomePage'}),
NavigationActions.navigate({ routeName: 'HomePage'})
]
});
this.props.navigation.dispatch(resetAction);
Replace - 用另一个路由替换指定的路由
- key - string - 被替换的路由的 key,如果未指定,最近的路由将会被替换
- newKey - string - 用于替换路线的 Key。 如果未提供,则自动生成。
- routeName - string - routeName用于替换路由。
- params - object - 要传入替换路由的参数。
- action - object - 可选的子动作。
- immediate* - boolean - 目前没有效果, 这是 stack navigator 支持动画替换(它目前不支持)的占位符。
Push - 在堆栈顶部添加一条路由,并导航至该路由. 与navigate的区别在于,如果有已经加载的页面,navigate方法将跳转到已经加载的页面,而不会重新创建一个新的页面。 push 总是会创建一个新的页面,所以一个页面可以被多次创建
- routeName - string - routeName用于替换路由。
- params - object - 将合并到目标路由的参数(通过this.props.navigation.state.params在目标路由获取)。
- action - Object - 可选 - (高级)如果页面是 navigator,则是在子路由器中运行的子操作。
import { StackActions } from 'react-navigation';
const pushAction = StackActions.push({
routeName: 'Profile',
params: {
myUserId: 9,
},
});
this.props.navigation.dispatch(pushAction);
The pop 一个可以返回到堆栈中上一个路由到方法,通过设置参数 n,可以指定返回的多少层。
- n - number - 返回的层数
import { StackActions } from 'react-navigation';
const popAction = StackActions.pop({
n: 1,
});
this.props.navigation.dispatch(popAction);
popToTop 一个可以直接跳转到堆栈最顶层,并销毁其它所有页面的方法,它在功能上与StackActions.pop({n:currentIndex})
相同。
import { StackActions } from 'react-navigation';
this.props.navigation.dispatch(StackActions.popToTop());
在导航器屏幕之外使用导航功能(巧用导航器的ref)
有一种场景:有的时候我们需要在导航器中所定义的屏幕之外使用导航器来做页面跳转。
- 屏幕之间的跳转是需要借助
navigation
来完成的; - 我们知道导航器中定义的屏幕可以通过
const {navigation} = this.props;
来获取navigation
; - 那么,如果我们在非导航器中所定义的屏幕中做屏幕跳转的关键一步,就是要想法获取
navigation
; - 那么,如何才能在非导航器中所定义的屏幕中获取到这个
navigation
呢?
下面就给大家讲解通过ref
属性还获得navigation
:
import { NavigationActions } from 'react-navigation';
const AppNavigator = StackNavigator(SomeAppRouteConfigs);
class App extends React.Component {
someEvent() {
// call navigate for AppNavigator here:
this. navigation && this. navigation.dispatch(
NavigationActions.navigate({ routeName: someRouteName })
);
}
render() {
return (
<AppNavigator ref={nav => { navigation = nav; }} />
);
}
}
上述代码通过导航器的顶级节点的
ref
属性获取到navigation
,当上述代码的AppNavigator
节点被渲染时,ref会被回调这是就可以获取到navigation
了,需要提醒大家的是,这种用法对除StackNavigator
之外的其他两种类型的导航器也是实用的哦;
- 大家在学习使用React Navigation3x过程中遇到任何问题都可以在React Navigation3x的视频教程中寻找答案哈。
- 另外,也可以通过最新版React Native+Redux打造高质量上线App视频教程学习React Navigation3x开发的更多实战经验和技巧,以及优化思路。
热门评论
react navigation不再集成redux了,有什么替代方案吗?