隨著react-navigation逐漸穩定,Navigator也被光榮的退休了。在React Native生態環境中需要一款可擴充套件且易於使用的導航元件,Navigator 自然勝任不了,這時React Native社群便孕育出了一個開源導航元件react-navigation。
react-navigation的出現替代了Navigator、 Ex-Navigation等老一代的導航元件,react-navigation可以說是Navigator的加強版,不僅有Navigator的全部功能,另外還支援底部導航類似於與iOS中的UITabBarController,此外它也支援側拉效果方式的導航類似於Android中的抽屜效果。
這篇文章將向大家分享react-navigation的一些實用技巧,以及從navigator到react-navigation的一些實戰經驗。另外大家也可以學習與本教程配套的視訊版:《全新導航器react-navigation精講》
什麼是導航器?
導航器也可以看成一個是普通的React元件,你可以通過導航器來定義你的App的導航結構。 導航器還可以渲染通用元素,例如可以配置的標題欄和選項卡欄。
在react-navigation中有以下三種型別的導航器:
- StackNavigator: 類似於普通的Navigator,螢幕上方導航欄;
- TabNavigator: 相當於iOS裡面的TabBarController,螢幕下方的標籤欄;
- DrawerNavigator: 抽屜效果,側邊滑出;
你可以通過以上三種導航器來建立你APP,可以是其中一個也可以多個組合,這個可以根據具體的應用場景並結合每一個導航器的特性進行選擇。
在開始學習三種導航器之前,我們需要先了解兩個和導航關於概念:
Screen navigation prop(螢幕導航屬性)
:通過navigation可以完成螢幕之間的排程操作,例如開啟另一個螢幕;Screen navigationOptions(螢幕導航選項)
: 通過navigationOptions可以定製導航器顯示螢幕的方式(例如:頭部標題,選項卡標籤等);
導航器所支援的Props
const SomeNav = StackNavigator/TabNavigator/DrawerNavigator({
// 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獲取到該資料。
Screen Navigation Prop(螢幕的navigation Prop)
當導航器中的螢幕被開啟時,它會收到一個navigation
prop,navigation
prop是整個導航環節的關鍵一員,接下來就詳細講解一下navigation
的作用。
navigation包含一下功能:
- navigate:跳轉到其他介面;
- state:螢幕的當前state;
- setParams:改變路由的params;
- goBack:關閉當前螢幕;
- dispatch:向路由傳送一個action;
注意:一個navigation有可能沒有navigate、setParams以及goBack,只有state與dispatch,所以在使用navigate時要進行判斷,如果沒有navigate可以使用navigation去dispatch一個新的action。如:
const {navigation,theme,selectedTab}=this.props;
const resetAction = NavigationActions.reset({
index: 0,
actions: [
NavigationActions.navigate({
routeName: 'HomePage',
params:{
theme:theme,
selectedTab:selectedTab
},
})
]
})
navigation.dispatch(resetAction)
複製程式碼
使用navigate進行介面之間的跳轉
- navigate(routeName, params, action)
- routeName:要跳轉到的介面的路由名,也就是在導航其中配置的路由名;
- params:要傳遞給下一個介面的引數;
- action:如果該介面是一個navigator的話,將執行這個sub-action。
export const AppStackNavigator = StackNavigator({
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>
)
}
}
複製程式碼
使用state的params
可以通過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 改變route params
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完成,下文會講到。
使用goBack返回到上一頁面或指定頁面
-
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傳送一個action
dispatch: function dispatch(action)
:給當前介面設定action,會替換原來的跳轉,回退等事件。
const resetAction = NavigationActions.reset({
index: 0,
actions: [
NavigationActions.navigate({
routeName: 'HomePage',
params:{
theme:theme,
selectedTab:selectedTab
},
})
]
})
navigation.dispatch(resetAction)
複製程式碼
NavigationActions
- Navigate : 導航到其他的頁面;
- Reset : 重置當前 state 到一個新的state;
- Back : 返回到上一個頁面;
- Set Params : 設定指定頁面的Params;
- Init : 初始化一個 state 如果 state 是 undefined;
Navigate:
Navigatie action會使用Navigate action的結果來更新當前的state。
- routeName:字串,必選項,在app的router裡註冊的導航目的地的routeName。
- params:物件,可選項,融合進目的地route的引數。
- actions:物件,可選項(高階),如果screen也是一個navigator,次級action可以在子router中執行。在文件中描述的任何actions都可以作為次級action。
import { NavigationActions } from 'react-navigation'
const navigateAction = NavigationActions.navigate({
routeName: 'Profile',
params: {},
action: NavigationActions.navigate({ routeName: 'SubProfileRoute'})
})
this.props.navigation.dispatch(navigateAction)
複製程式碼
Reset:
Reset action刪掉所有的navigation state並且使用這個actions的結果來代替。
- index,number,必選,navigation state中route陣列中啟用route的index。
- actions,array,必選項,Navigation Actions陣列,將會替代navigation state。
import { NavigationActions } from 'react-navigation'
const resetAction = NavigationActions.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 } from 'react-navigation'
const resetAction = NavigationActions.reset({
index: 1,
actions: [
NavigationActions.navigate({ routeName: 'WelcomePage'}),
NavigationActions.navigate({ routeName: 'HomePage'})
]
});
this.props.navigation.dispatch(resetAction);
複製程式碼
Back
返回到前一個screen並且關閉當前screen.backaction creator接受一個可選的引數:
- key:這個可以和上文中講到的goBack的key是一個概念;
import { NavigationActions } from 'react-navigation'
const backAction = NavigationActions.back();
this.props.navigation.dispatch(backAction);
複製程式碼
SetParams
通過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;
還有那些應用場景?
在導航器螢幕之外使用導航功能(巧用導航器的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
之外的其他兩種型別的導航器也是實用的哦;