React Navigation V5、V6版本使用方式基本相同鹏控,只是改了部分屬性,參數(shù)肤寝。
安裝
核心:提供底層 API当辐,可在此基礎(chǔ)上實現(xiàn)各種導(dǎo)航形式
yarn add react-native-screens react-native-safe-area-context @react-navigation/native
- react-native-screens: 用于原生層釋放未展示的頁面,改善 app 內(nèi)存使用
- react-native-safe-area-context: 用于保證頁面顯示在安全區(qū)域(主要針對劉海屏)
-
@react-navigation/native
: 為 React Navigation 的核心
修改 MainActivity.java
添加以下代碼鲤看,否則 Android 下可能在某些情況下造成 App 崩潰缘揪,比如調(diào)整系統(tǒng)字體縮放/修改 APP 權(quán)限配置后再次返回 App,若沒有以下修改义桂,App 崩潰找筝。即使設(shè)置了,還是有問題慷吊,無法保持最后查看的頁面袖裕, App 會重新加載 js Bundle,返回到首頁(是使用 3.10.1
版本在 debug 模式下發(fā)現(xiàn)的該問題溉瓶,其他情況還需實際測試)
....
import android.os.Bundle;
public class MainActivity extends ReactActivity {
....
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(null);
}
}
在 App.js 中添加以下代碼急鳄,激活 react-native-screens
的原生端(最新版本默認(rèn)已為激活狀態(tài)谤民,可省略該步驟)
import { enableScreens } from 'react-native-screens';
enableScreens();
Stack:相當(dāng)于路由,如果不是僅需的 Tab 或 Drawer攒岛,必裝
yarn add @react-native-masked-view/masked-view react-native-gesture-handler @react-navigation/stack
- @react-native-masked-view/masked-view: 用在頭部導(dǎo)航欄中返回按鈕的顏色設(shè)置
- react-native-gesture-handler: 用于支持手勢切換頁面
-
@react-navigation/stack
: Stack Navigator
安裝完之后赖临,在 js 入口文件,如 index.js 頂部添加 import 'react-native-gesture-handler';
灾锯,少了這一句兢榨,可能會導(dǎo)致生產(chǎn)環(huán)境 app 出現(xiàn)閃退現(xiàn)象。
其他:官方提供的幾種導(dǎo)航器顺饮,根據(jù)需要安裝吵聪,也可以參考自行建構(gòu)
-
Drawer:
yarn add @react-navigation/drawer react-native-gesture-handler react-native-reanimated
-
Bottom Tabs:
yarn add @react-navigation/bottom-tabs
-
Material Bottom Tabs:
yarn add @react-navigation/material-bottom-tabs react-native-paper react-native-vector-icons
-
Material Top Tabs:
yarn add @react-navigation/material-top-tabs react-native-tab-view react-native-pager-view
安裝所需導(dǎo)航器并安裝相應(yīng)依賴,有些依賴可能有重復(fù)兼雄,安裝一次就行了吟逝,比如 Drawer
與 Stack
都依賴 react-native-gesture-handler
,安裝一次即可赦肋。
最后
react-native-screens
需要修改 Android 平臺的 MainActivity.java
块攒,不再需要其他操作了
// 頂部添加
import android.os.Bundle;
// 主體 class 中添加
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(null);
}
對于 iOS,需要在項目根目錄執(zhí)行 npx pod-install
安裝原生組件的依賴
使用
先看以下一段偽代碼了解 React Navigation
的使用方法
import { NavigationContainer } from '@react-navigation/native';
import { createStackNavigator } from '@react-navigation/stack';
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
import { createMaterialTopTabNavigator } from '@react-navigation/material-top-tabs';
const Stack = createStackNavigator()
const Tab = createBottomTabNavigator();
const Top = createMaterialTopTabNavigator();
const First = () => (<Top.Navigator ...props>
<Top.Screen ...props/>
<Top.Screen ...props/>
</Top.Navigator>);
const Main = () => (<Tab.Navigator ...props>
<Tab.Screen ...props component={First}/>
<Tab.Screen ...props/>
</Tab.Navigator>);
const App = () => (<NavigationContainer ...props>
<Stack.Navigator ...props>
<Stack.Screen ...props component={Main}/>
<Stack.Screen ...props/>
</Stack.Navigator>
</NavigationContainer>);
- 導(dǎo)航器總是使用
<Nav.Navigator> <Nav.Screen/> </Nav.Navigator>
格式組合頁面佃乘,其中Nav
可以使用官方提供的幾個導(dǎo)航器stack
囱井、drawer
、bottom-tabs
趣避、material-bottom-tabs
庞呕、material-top-tabs
,當(dāng)然也可參考自行建構(gòu)程帕。 - 導(dǎo)航器本身可以作為另外一個導(dǎo)航器的
Screen component
住练,即當(dāng)作一個普通頁面作為另外一個導(dǎo)航器的子頁面。 - 最終使用
NavigationContainer
包裹最頂層導(dǎo)航器愁拭。
一般使用Stack
作為頂層導(dǎo)航器(如上的例子)讲逛,在 Tab 內(nèi)打開頁面會覆蓋整個屏幕,退回后才能進(jìn)行 Tab 切換岭埠。
當(dāng)然也可以使用Tab
作為頂層導(dǎo)航器妆绞,切換頁面時 TabBar 不會被覆蓋,每個 Tab 都有獨立的堆棧枫攀。
React Navigation
對 NavigationContainer
、 Nav.Navigator
株茶、 Nav.Screen
提供了豐富的配置選項来涨,做個簡單介紹
一、 NavigationContainer
該組件在 @react-navigation/native
中定義启盛,一般情況蹦掐,一個 APP 只有一個技羔,參考 官方文檔,該組件支持以下屬性
1. theme
主題卧抗,該屬性由 @react-navigation/native
緩存藤滥,但并未直接起作用,而是會下發(fā)到導(dǎo)航器社裆,由導(dǎo)航器獲取并加以利用拙绊,默認(rèn)提供了 淺色 和 深色 兩組屬性 屬性使用情況如下:
theme={
dark: false,
colors: {
// 文字顏色: Header 標(biāo)題 / BottomTab 未激活文字
text: 'rgb(28, 28, 30)',
// 激活顏色: BottomTab 激活文字 / iOS Header 返回上一頁文字
primary: 'rgb(99, 164, 252)',
// 區(qū)塊背景色: 如 Header, TopTab, BottomTab 背景
card: 'rgb(255, 255, 255)',
// 邊框顏色: 如 Header, TopTab, BottomTab 邊框
border: 'rgb(216, 216, 216)',
// 背景色: 頁面整體背景顏色
background: 'rgb(242, 242, 242)',
// 提醒色: BottomTab 角標(biāo)背景
notification: 'rgb(255, 59, 48)',
},
}
2. ref
獲取 NavigationContainer
實例,用于調(diào)用實例 api泳秀,可通過 console.log 打印可用 api
3. initialState
自定義傳入變量标沪,多用于 deepLink,該項暫未驗證
4. onStateChange
導(dǎo)航狀態(tài)變化的監(jiān)聽函數(shù)嗜傅,可用于頁面統(tǒng)計或其他操作
5. onReady
V6 版本新增金句,容器加載并渲染完畢時的回調(diào),僅會觸發(fā)一次吕嘀。此時可安全的使用 ref
調(diào)用 API违寞,也可在此時隱藏開屏頁
6. linking
V6 版本新增,用于 deepLink
7. children
子組件(導(dǎo)航器 Navigator
組件)偶房,該項一般使用 jsx 直接插入趁曼,而不是通過 props 傳遞,比如上面的示例蝴悉,children
為 Stack.Navigator
二彰阴、Nav.Navigator
導(dǎo)航器根組件谴分,用于包裹導(dǎo)航器下的頁面肄满;通過閱讀源碼可知道所有導(dǎo)航器都支持4個屬性:
-
@react-navigation/routers
定義的initialRouteName
-
@react-navigation/core
定義的children
/screenOptions
/screenListeners
1. initialRouteName
導(dǎo)航器默認(rèn)要顯示的 screen
2. children
導(dǎo)航器包裹的 screens,通常不會使用 Props 傳遞允瞧,而是在 jsx 中實現(xiàn)庆杜。
3. screenOptions
不同類型的導(dǎo)航器包裹的 screen 支持的屬性是一樣的射众,都會有一個 options
屬性,此處設(shè)置的 screenOptions
與 screen.options
屬性完全相同晃财,作為所有 screen 的 options
默認(rèn)值叨橱。
閱讀 @react-navigation/core
源碼和文檔,這個參數(shù)的值可以是 Object
或 Function({route, navigation}) => Object
断盛,且未對 Object 字段做任何限制罗洗,而只是為導(dǎo)航器的實現(xiàn)提供了一個頂層 API,比如官方的兩個實現(xiàn)支持不同的 options:
-
@react-navigation/stack
->Stack.Navigator
的 screenOptions可用屬性 -
@react-navigation/bottom-tabs
->Tab.Navigator
的 screenOptions可用屬性
// 直接設(shè)置為 Object
<Nav.Navigator
screenOptions = {{
title, header, headerShown, ........
}}
>
<Nav.Screen ...props />
</Nav.Navigator>
// 或通過函數(shù)返回钢猛,比如大部分 screen 所需屬性相同伙菜,僅在函數(shù)內(nèi)對特別的 screen 做處理
<Nav.Navigator
screenOptions = { ({route, navigation}) => {
return { title, header, headerShown, ........}
}}
>
<Nav.Screen ...props />
</Nav.Navigator>
在 V6 版本官方還提供了一個 Nav.Group
對具有相同屬性的 screen 進(jìn)行分組批量設(shè)置
<Nav.Navigator>
<Nav.Group screenOptions={{}}>
<Nav.Screen/>
<Nav.Screen />
</Nav.Group>
<Nav.Group screenOptions={{}}>
<Nav.Screen/>
<Nav.Screen />
</Nav.Group>
</Nav.Navigator>
4. screenListeners
監(jiān)聽導(dǎo)航器發(fā)送的事件消息,所有導(dǎo)航器共有的消息類型有 focus
/ blur
/ beforeRemove
/ state
(文檔)命迈,不同導(dǎo)航器還有特有消息贩绕,如:
該屬性的值與 screenOptions
有點類似,也可以指定為 Object
或 Function
淑倾,如
// 直接設(shè)置為 Object
<Nav.Navigator
screenListeners={{
focus: () => {},
state: (e) => { console.log('state changed', e.data);},
}}
>
<Nav.Screen ...props />
</Nav.Navigator>
// 或通過函數(shù)返回 要綁定的 監(jiān)聽函數(shù)
<Nav.Navigator
listeners={({ navigation, route }) => ({
return {
focus: () => {},
state: (e) => { console.log('state changed', e.data);},
}
}}
>
<Nav.Screen ...props />
</Nav.Navigator>
以上四項為基礎(chǔ)屬性馏鹤,適用于所有導(dǎo)航器,不同的導(dǎo)航器會在此基礎(chǔ)中拓展額外的屬性:
5. @react-navigation/stack
detachInactiveScreens
/ 屬性(文檔)keyboardHandlingEnabled
/ mode
/ headerMode
Stack.Navigator
的部分屬性現(xiàn)在已經(jīng)或即將轉(zhuǎn)移到 options
中娇哆,具體支持哪些屬性需以官方文檔為準(zhǔn)湃累,下面介紹 stack
的 options
會提到目前的變化。
6. @react-navigation/bottom-tabs
detachInactiveScreens
/ backBehavior
(繼承自 @react-navigation/routers
) / sceneContainerStyle
/ tabBar
/ (V6版這兩個屬性移動到了 options 中配置) 屬性(文檔)lazy
/ tabBarOptions
三迂尝、Nav.Screen
導(dǎo)航器內(nèi)的具體頁面脱茉,該組件是在 @react-navigation/core
中實現(xiàn)的,與導(dǎo)航器類型無關(guān)垄开,所有類型的導(dǎo)航器 screen 都支持且僅支持以下屬性
1. name
頁面名稱琴许,可用于導(dǎo)航跳轉(zhuǎn)
2. options
與 Nav.Navigator
中的 screenOptions
相同,單獨設(shè)置來覆蓋 screenOptions
的配置溉躲,僅針對當(dāng)前頁面榜田;同樣的,可以設(shè)置為 Object
或通過 Function
返回锻梳;具體結(jié)構(gòu)由 Screen 所屬的 Navigator 類型決定箭券。
3. listeners
與 Nav.Navigator
中的 screenListeners
相同,可以設(shè)置為 Object
或通過 Function
返回疑枯;僅對當(dāng)前頁面進(jìn)行監(jiān)聽辩块,不會覆蓋 Nav.Navigator
中的設(shè)置,即二者都會被觸發(fā)荆永。
4. initialParams
傳遞給 Screen
組件的初始化 params废亭,可在 screen 內(nèi)獲取,從而顯示不同數(shù)據(jù)具钥。
5. getId
屬性值為 Function(initialParams) => string
豆村,返回一個唯一 ID,在多個頁面有相同 name
屬性時骂删,可在使用 navigate('ScreenName', params)
時通過指定 params.userId
跳轉(zhuǎn)到預(yù)期頁面掌动。
6. component / getComponent / children
Screen
綁定的組件, 可通過 component
指定組件對象宁玫,getComponent
回調(diào)返回組件粗恢,或直接使用 children
定義組件;三者互斥欧瘪,一般使用 component
屬性來定義
<Nav.Screen component={Screen} />
<Nav.Screen getComponent={() => require('./Screen').default} />
<Nav.Screen>
{(props) => <Screen {...props} />}
</Nav.Screen>
四适滓、頁面內(nèi)
通過以上文檔可以看出,頁面的 options
/ listeners
都是由上層代碼控制,不過 React Navigation
也提供了相關(guān)接口凭迹,可直接在 Screen
組件內(nèi)部維護(hù)。
// 函數(shù)式組件: react navigation 會傳遞 navigation / route 兩個參數(shù)
function Screen({ navigation, route }) {
// 在頁面顯示之前設(shè)(重)置 options 值苦囱,相當(dāng)于在 componentDidMount 階段執(zhí)行
// useLayoutEffect 是阻塞同步的嗅绸,即執(zhí)行完此處之后,才會繼續(xù)向下執(zhí)行
React.useLayoutEffect(() => {
navigation.setOptions({
title:'....'
});
}, [navigation]);
// 綁定 listener, useEffect 是異步執(zhí)行的撕彤,不會阻塞
React.useEffect(() => {
const unsubscribe = navigation.addListener('focus', () => {
// do something
});
// 返回卸載監(jiān)聽的方法鱼鸠,以便在當(dāng)前組件注射時取消監(jiān)聽
return unsubscribe;
}, [navigation]);
// 頁面內(nèi)容
return <ScreenContent />;
}
// Class 組件:navigation / route 以 props 參數(shù)傳遞
class Screen extends React.Component {
componentDidMount() {
const {navigation} = this.props;
// render 后,未顯示前羹铅,設(shè)置 options
navigation.setOptions({
title:'....'
});
// 綁定監(jiān)聽函數(shù)
this._unsubscribe = navigation.addListener('focus', () => {
// do something
});
}
componentWillUnmount() {
// 組件注銷時蚀狰,取消監(jiān)聽
this._unsubscribe();
}
render() {
return <ScreenContent />;
}
}
在 react navigation V4 之前的版本,還提供了一個 navigationOptions
靜態(tài)變量用于設(shè)置 options职员,V5 版本之后移除了該特性麻蹋,如果要繼續(xù)使用,可使用以下方法
class Home extends React.Component {
static navigationOptions = {
title:'....'
};
}
function Screen() {
}
Screen.navigationOptions = {
title:'....'
};
// V4 版本無需額外設(shè)置了焊切,react navigation 默認(rèn)已支持扮授,對于 V5 之后版本
<Nav.Navigator>
<Nav.Screen component={Home} options={Home.navigationOptions} />
<Nav.Screen component={Screen} options={Screen.navigationOptions} />
</Nav.Navigator>
當(dāng)然,也可以使用同樣的方法自定義一個 static listener专肪,但 react navigation 不推薦使用這種方式了刹勃,所以才會從內(nèi)核中移除了該特性,因為這種方式有以下弊端(如果不在意以下弊端嚎尤,仍然這么使用也是可以的):
- 如果是高階組件荔仁,靜態(tài)屬性需要額外的代碼才能工作
- 無法使用 props 和 context 的能力,靈活性變差
- 無法自動進(jìn)行類型檢查芽死,你需要手動給這個屬性添加注解
- 在 Fast Refresh 下有問題乏梁,具體來說是修改它無法觸發(fā)重新渲染
五、StackNavigator.Screen options
這是最常用的導(dǎo)航器收奔,幾乎是必備的掌呜,通常情況下,使用默認(rèn)的配置即可取得不錯的效果坪哄,不過 Stack
為了更加靈活质蕉,提供了很多 options
自定義屬性,這里對其做一個整理(若無特殊說明翩肌,則代表為 V5/V6 都支持的屬性)
最基本屬性
-
title
: 設(shè)置為 string, 會作為stack
導(dǎo)航器標(biāo)題文字headerTitle
的 fallback -
keyboardHandlingEnabled
: 切換頁面時是否自動隱藏已打開的鍵盤模暗,默認(rèn)為true
(V6 新增,從 V5 版本的Stack.Navigator
屬性移動到了這里) -
presentation
: 頁面模式念祭,支持card
(默認(rèn)) /modal
/transparentModal
(V6 新增兑宇,該屬性相當(dāng)于 V5 版本Stack.Navigator
的mode
屬性轉(zhuǎn)移到了這里),該值較為重要粱坤,會在下面單獨說明隶糕。 -
detachPreviousScreen
: V6 新增瓷产,是否在頁面切換后注銷上一個頁面以節(jié)省內(nèi)存。默認(rèn)情況下枚驻,若新頁面為 modal 模式濒旦,該值為false
,否則為true
再登。即下一個頁面未鋪滿全屏尔邓,當(dāng)前頁面仍會顯示部分,就應(yīng)該設(shè)置為false
锉矢。
注意:該值只有在Stack.Navigator
屬性值detachInactiveScreens=true
(默認(rèn))時才生效梯嗽,該值一般無需手動設(shè)置,會根據(jù)頁面presentation
等屬性值自動設(shè)置合適的值沽损。
與 Header 組件相關(guān)的屬性
V6 版與 V5 版相比灯节,將 Header 相關(guān)組件從 stack
包中提取出來組合為一個 Elements
包,該包提供了 Header
缠俺、HeaderBackground
显晶、HeaderTitle
、HeaderBackButton
壹士、MissingIcon
磷雇、PlatformPressable
、ResourceSavingView
組件和一些工具函數(shù)躏救。Header
組件并未直接支持 left / right唯笙,stack
導(dǎo)航器使用該 Header
擴(kuò)充了左側(cè)組件,并額外支持一些其他屬性盒使。這樣做的好處是崩掘,更利于自定義 Header,可利用 Elements
中組件自定義整個 Header少办,或僅自定義 Header 左(右)側(cè)組件苞慢。以下說明中:
- 無特殊標(biāo)記,則代表是
Elements/Header
組件直接支持的屬性英妓,V5/V6通用 -
+
: V6 版新增屬性(仍是Elements/Header
直接支持的屬性) -
*
: V5/V6通用(由stack
擴(kuò)充而非Elements/Header
直接支持的屬性) -
*+
: V6 新增屬性(由stack
擴(kuò)充而非Elements/Header
直接支持的屬性)
標(biāo)題欄整體屬性
-
headerStyle
: 自定義標(biāo)題欄樣式挽放,如果要改變高度,應(yīng)直接使用height:Number
設(shè)置蔓纠,不要通過布局方式設(shè)置一個不確定的高度辑畦,該值對于頁面切換時的 Header 動效非常重要。 -
headerTransparent
: 標(biāo)題欄是否透明腿倚,與headerStyle
中直接設(shè)置backgroundColor
的不同在于:這里設(shè)置透明纯出,會使頁面的marginTop
為 0,此時需要定義headerBackground
組件來遮擋。 -
headerBackground
: 標(biāo)題欄背景組件暂筝,配合headerTransparent
使用的箩言,可以用來實現(xiàn)諸如毛玻璃 Header 的效果。 -
headerStatusBarHeight
: 手動設(shè)置 statusBar 高度焕襟,Header 組件會 paddingTop 這個值以保證在劉海屏機(jī)型也可以正常使用分扎,默認(rèn)會由系統(tǒng)自動獲取。 -
headerPressColor
(V5版名稱:): 點擊 Header 內(nèi)按鈕組件的水波紋顏色胧洒,僅對 Android 5 及以上headerPressColorAndroid
- +
headerPressOpacity
: 點擊 Header 內(nèi)按鈕組件的透明度,對 iOS 和 Android 5 以下 -
headerTintColor
: 設(shè)置標(biāo)題色調(diào)(該屬性和headerPressColor
/headerPressOpacity
會傳遞給 Header 的各子組件使用墨状,比如標(biāo)題就會使用該屬性設(shè)置文字顏色卫漫,按鈕則使用到另兩個屬性) - *
header
: 自定義標(biāo)題欄組件,定義為函數(shù)肾砂,返回一個 RN 組件列赎;設(shè)置該屬性,即不再使用默認(rèn) Header镐确。 - *
headerShown
: 是否顯示標(biāo)題欄 - *+
headerMode
: 標(biāo)題欄顯示模式包吝,支持 "float"(iOS默認(rèn)值)、"screen"(非 iOS 默認(rèn)值)(該屬性 V5 版是在Nav.Navigator
屬性中設(shè)置源葫,V6 轉(zhuǎn)移到了這里) -
: 安全區(qū)域設(shè)置(針對劉海屏機(jī)型)诗越,默認(rèn)情況下會自動設(shè)置,但可以通過該屬性通過safeAreaInsets
{left, right, top, bottom}
手動設(shè)置息堂,自定義設(shè)置注意考慮橫豎屏的情況嚷狞。(該屬性僅 V5 支持,V6 已移除荣堰,設(shè)置安全區(qū)域可參考 官方文檔床未、用法說明)
標(biāo)題組件
-
headerTitleAlign
: 標(biāo)題對齊方式,支持left
(Android 默認(rèn)) /center
(iOS 默認(rèn)) -
headerTitleAllowFontScaling
: 標(biāo)題文字是否隨系統(tǒng)文字大小縮放 -
headerTitleStyle
: 自定義標(biāo)題文字的樣式 -
headerTitleContainerStyle
: 自定義標(biāo)題文字所在 View 容器的樣式 -
headerTitle
: 標(biāo)題振坚,可直接設(shè)置文字薇搁,未設(shè)置則使用title
屬性;也可以設(shè)置為函數(shù)渡八,返回一個組件啃洋,函數(shù)參數(shù)為{allowFontScaling, style, children}
,這三個參數(shù)是由上面屬性結(jié)合而來呀狼。
左側(cè)返回組件
-
headerLeft
: 自定義 Header 左側(cè)組件裂允,props 會傳遞 options 設(shè)置 -
headerLeftContainerStyle
: 自定義包裹 Header 左側(cè)組件容器的樣式 - *
headerBackImage
: 返回鍵,設(shè)置為一個函數(shù)哥艇,返回“返回鍵”組件绝编,函數(shù)參數(shù)為{tintColor:"標(biāo)題顏色"}
- *
headerBackTitle
: 返回鍵右側(cè)的文字 - *
headerTruncatedBackTitle
: 返回鍵右側(cè)文字過長,標(biāo)題欄無法顯示時的替代返回文字,默認(rèn): "Back" - *
headerBackAllowFontScaling
: 返回文字是否隨系統(tǒng)文字大小縮放 - *
headerBackTitleStyle
: 自定義返回文字樣式 - *
headerBackTitleVisible
: 是否顯示返回文字十饥,Android 默認(rèn) false窟勃,iOS 默認(rèn) true - *
headerBackAccessibilityLabel
: 返回鍵的無障礙標(biāo)簽
右側(cè)自定義組件
-
headerRight
: 自定義 Header 右側(cè)組件,指定為函數(shù) 或 RN組件逗堵,props 會傳遞 options 設(shè)置 -
headerRightContainerStyle
: 自定義包裹 Header 右側(cè)組件容器的樣式
與頁面組件相關(guān)的屬性
-
cardStyle
: 頁面 Card 的樣式 -
cardShadowEnabled
: 是否在切換頁面時顯示頁面邊緣的陰影秉氧,默認(rèn)為false
,啟用陰影需要當(dāng)前頁面背景不能為透明 +cardStyleInterpolator
屬性返回了shadowStyle
樣式蜒秤,默認(rèn)只有SlideFromRightIOS
動效支持且僅支持 iOS汁咏,因為陰影組件 Animated.View 的默認(rèn)樣式是使用 shadowStyle 實現(xiàn)的,該類型 style 僅支持 iOS -
cardOverlayEnabled
: 是否在 Card 下方添加一個 overlay 組件(即在前一個 Card 的上方添加)作媚,iOS 默認(rèn)為false
攘滩,Android
在presentation="transparentModal"
為false
,否則為true
-
cardOverlay
: 函數(shù)纸泡,返回cardOverlayEnabled=true
要覆蓋的組件漂问,該組件可用于頁面切換時的效果設(shè)定,比如一個黑色的 view女揭,切換過程中逐漸透明蚤假,甚至是毛玻璃組件,下方頁面就呈現(xiàn)出一種逐漸顯示的效果吧兔。
與頁面切換手勢相關(guān)的屬性
-
gestureEnabled
: 是否啟用手勢返回磷仰,iOS默認(rèn)開啟(不開啟的話只能在頁面上自定義返回按鈕了),Android 默認(rèn)是關(guān)閉的(Android 除了返回按鈕掩驱,還有物理/虛擬返回鍵) -
gestureDirection
: 返回的手勢滑動方向芒划,支持以下值-
horizontal
: 從左到右 -
horizontal-inverted
: 從右到左 -
vertical
: 從上到下 -
vertical-inverted
: 從下到上
-
-
gestureResponseDistance
: 從邊緣為起點,支持手勢返回的距離欧穴,格式為{horizontal:50, vertical:135}
民逼;比如手勢方向gestureDirection
為horizontal
,那么只有在左邊緣 50 以內(nèi)的區(qū)域向右滑動才會響應(yīng)涮帘。 -
gestureVelocityImpact
: 觸摸返回的手速設(shè)置拼苍,在手速低于該值時,滑動距離需大于滑動方向上尺寸的 50% 才會返回到上一頁调缨,否則彈回疮鲫;高于所設(shè)置手速,即使滑動距離未達(dá)到50%弦叶,也會返回到上一頁面俊犯;默認(rèn)值為 0.3
與頁面切換效果相關(guān)的屬性
-
animationEnabled
: 是否使用頁面切換動效,在 Android 和 iOS 默認(rèn)為true
伤哺,Web 為false
-
animationTypeForReplace
: 切換動畫的方式:支持 "push"(默認(rèn)) 和 "pop" -
transitionSpec
: 切換頁面的動效配置 -
cardStyleInterpolator
: 切換頁面時 Screen Card 的樣式 -
headerStyleInterpolator
: 切換頁面時 Screen Header 的樣式
切換動效
由以上屬性可以看出燕侠,頁面切換效果由以下屬性共同構(gòu)成:
transitionSpec
cardStyleInterpolator
headerStyleInterpolator
gestureDirection
前三個用于實現(xiàn)切換動效和樣式者祖,gestureDirection
用于在 gestureEnabled=true
(iOS默認(rèn)為 true) 時配合動效,比如切換為上下展開收縮绢彤,gestureDirection
則應(yīng)該支持上下滑動的手勢七问。設(shè)置自定義動效可使用如下結(jié)構(gòu)的代碼:
const transition = {
gestureDirection:"horizontal",
transitionSpec: {},
cardStyleInterpolator:() => {},
headerStyleInterpolator:() => {},
}
<Stack.Navigator
screenOptions={
cardStyle:{},
gestureEnabled:true,
...transition
}
>
<Stack.Screen />
</Stack.Navigator>
React Navigation 的設(shè)計初衷應(yīng)該也在于此,所以已默認(rèn)提供了幾組屬性茫舶,可以直接使用械巡。
-
BottomSheetAndroid
: 半透明到不透明, 從底部滑入 -
FadeFromBottomAndroid
: 半透明到不透明, 從距離頂部一小段距離的位置滑至頂部 -
ModalFadeTransition
: 無運動, 僅半透明到不透明 -
ModalPresentationIOS
: 無透明度變化, 從底部滑倒接近頂部, 以卡片形式彈窗, 下方頁面會縮小 -
ModalSlideFromBottomIOS
: 無透明度變化, 從底部滑倒頂部 -
RevealFromBottomAndroid
: 無透明度變化, 新頁面從底部逐漸展開 -
ScaleFromCenterAndroid
: 透明到不透明, 從中心點爆炸式彈出 -
SlideFromRightIOS
: 無透明度變化, 從右側(cè)滑入(只有該效果實現(xiàn)了cardShadowEnabled
且僅支持 iOS) -
ModalTransition
: iOS 為ModalPresentationIOS
, Android 為BottomSheetAndroid
-
DefaultTransition
: iOS 為SlideFromRightIOS
, Android 為ScaleFromCenterAndroid
(API >= 29)、RevealFromBottomAndroid
(API = 28)饶氏、FadeFromBottomAndroid
(API < 28)
對于以上切換效果讥耗,有以下特點
- 對于
headerMode="float"
, 以上運動除ModalPresentationIOS
會強(qiáng)制修改headerMode="screen"
外,其他切換效果都是僅在頁面內(nèi)容區(qū)發(fā)生疹启,而不是整個頁面葛账;頁面 Header 會保持獨立的運動,若前一個頁面沒有 Header皮仁,會從右側(cè)滑入,否則會漸顯式替換前一個 Header菲宴。 - iOS 默認(rèn)啟用了手勢切換贷祈,所以
IOS
結(jié)尾的切換效果都沒有透明度變化,適合手勢切換喝峦,但也同樣可以用于 Android势誊。但反過來則不行,非IOS
結(jié)尾的切換效果由于有透明度變化谣蠢,不適合用于手勢切換粟耻。
使用方法:
import { TransitionPresets } from '@react-navigation/stack';
<Stack.Navigator
screenOptions={
cardStyle:{},
...TransitionPresets.SlideFromRightIOS,
}
>
<Stack.Screen />
</Stack.Navigator>
若對這些默認(rèn)提供的效果都不滿意,那只能自定義了眉踱。
1挤忙、transitionSpec
transitionSpec
需要提供 open
/ close
兩個配置,每個配置需包含 animation
/ config
兩個屬性谈喳。 其中 config
根據(jù) animation
類型進(jìn)行配置册烈。可參考 timing 婿禽、spring
const config = {
// 一般就兩種
animation: 'timing || spring',
// 根據(jù) animation 值提供配置
config: {
// animation="timing" 支持:
duration:1000,
easing: Easing.ease,
// animation="spring" 支持:
stiffness: 1000,
damping: 500,
mass: 3,
overshootClamping: true,
restDisplacementThreshold: 0.01,
restSpeedThreshold: 0.01,
},
};
const transitionSpec = {
open: config, // 新頁面彈出時動效
close: config, // 新頁面收回時動效赏僧,一般二者為同一個
};
// React Navigation 提供了幾個默認(rèn)的,可直接使用或作為參考
import { TransitionSpecs } from '@react-navigation/stack';
transitionSpec = TransitionSpecs.TransitionIOSSpec
transitionSpec = TransitionSpecs.FadeInFromBottomAndroidSpec
transitionSpec = TransitionSpecs.FadeOutToBottomAndroidSpec
transitionSpec = TransitionSpecs.RevealFromBottomAndroidSpec
2扭倾、cardStyleInterpolator
通過函數(shù)返回以下樣式
-
containerStyle
: Card 所在Animated.View
容器的樣式 -
cardStyle
: Card 組件 (Animated.View
) 樣式 -
overlayStyle
: 在cardOverlayEnabled=true
時淀零,由cardOverlay
組件的樣式 -
shadowStyle
: 在cardShadowEnabled=true
時,Card 邊緣的Animated.View
組件樣式
cardStyleInterpolator = ({
current, //當(dāng)前頁面值膛壹,如 current.progress 進(jìn)度
next, //切換后的頁面值驾中,如 next.progress 進(jìn)度
index, //card 在 stack 堆棧中的序號
closing, //是關(guān)閉還是打開 1 or 0
layouts //布局尺寸 {screen}
}) => {
return {
containerStyle:{},
cardStyle:{},
overlayStyle:{},
shadowStyle:{},
}
}
// React Navigation 提供了幾個默認(rèn)的唉堪,可直接使用或作為參考
import { CardStyleInterpolators } from '@react-navigation/stack';
cardStyleInterpolator = CardStyleInterpolators.forHorizontalIOS
cardStyleInterpolator = CardStyleInterpolators.forVerticalIOS
cardStyleInterpolator = CardStyleInterpolators.forModalPresentationIOS
cardStyleInterpolator = CardStyleInterpolators.forFadeFromBottomAndroid
cardStyleInterpolator = CardStyleInterpolators.forRevealFromBottomAndroid
3、HeaderStyleInterpolators
通過函數(shù)返回以下樣式
-
leftLabelStyle
: Header 返回鍵旁邊的"返回"文字所在Animated.Text
的樣式 -
leftButtonStyle
: Header 左側(cè)返回鍵外層的Animated.View
容器的樣式 -
rightButtonStyle
: Header 右側(cè)Animated.View
容器的樣式 -
titleStyle
: Header 標(biāo)題所在Animated.View
容器的樣式 -
backgroundStyle
: Header 背景組件的樣式
HeaderStyleInterpolators = ({
current, //當(dāng)前頁面值哀卫,如 current.progress 進(jìn)度
next, //切換后的頁面值巨坊,如 next.progress 進(jìn)度
layouts //布局尺寸: {screen, title, leftLabel}
}) => {
return {
leftLabelStyle:{},
leftButtonStyle:{},
rightButtonStyle:{},
titleStyle:{},
backgroundStyle:{},
}
}
// React Navigation 提供了幾個默認(rèn)的,可直接使用或作為參考
import { HeaderStyleInterpolators } from '@react-navigation/stack';
HeaderStyleInterpolators = HeaderStyleInterpolators.forUIKit
HeaderStyleInterpolators = HeaderStyleInterpolators.forFade
HeaderStyleInterpolators = HeaderStyleInterpolators.forStatic
以上三個屬性可全部自定義此改,也可以部分自定義 + 部分使用 React Navigation 提供的預(yù)置趾撵,最后再添加一個 gestureDirection
屬性就可構(gòu)成一組自定義頁面切換效果,非常的方便共啃。
頁面模式
以上便是 Stack.Screen
的 options
屬性支持的所有配置占调,最后再對影響頁面效果較大的 headerMode
、presentation
配置稍作說明移剪,headerMode
支持的兩個值:
-
float
: 此時頁面 Header 與頁面 Card 是分離的究珊,有一個 Header 容器組件總是在頂部,所有頁面的 Header 都在這個容器里纵苛,這種模式下剿涮,在切換頁面時, Header 與 Card 可以獨立執(zhí)行各自的切換動效攻人,比如模擬 iOS 原生效果取试。 -
screen
: 每個頁面的 Header 都在各自的 Card 頂部,即每個頁面整體獨立怀吻。切換頁面時瞬浓,是整個頁面進(jìn)行動效過渡。 -
: 該模式在 V6 版已移除蓬坡,使用none
headerShown=false
替代
presentation
配置更像一個快捷方式猿棉,修改該值,可能會自動設(shè)置 cardOverlayEnabled
屑咳、detachPreviousScreen
萨赁、headerMode
、gestureDirection
兆龙、transitionSpec
等屬性的默認(rèn)值用以配合效果位迂,但如果這些值手動設(shè)置了值,將不會自動配置详瑞,而是使用手動設(shè)置的值掂林,若設(shè)置為 transparentModal
,默認(rèn) cardStyle
的背景將修改為透明坝橡。 支持以下三個值:
-
card
: 頁面切換為模擬原生的效果泻帮,iOS 為 Header 漸隱漸顯/Card左右顯示隱藏,Android 為整體由下向上顯示(默認(rèn)值) -
modal
: 無論任何平臺计寇,都設(shè)置為頁面整體由下向上滑動顯示(與card
模式下的 Android 由下向上的動效不同)-
headerMode
自動設(shè)置為screen
- 動效也會自動設(shè)置用以配合 modal 頁面切換效果
-
-
transparentModal
: 與modal
類似-
headerMode
自動設(shè)置為screen
- 屏幕背景會設(shè)置為透明锣杂,因此可以看到上一個頁面
- 自動設(shè)置
detachPreviousScreen=false
保持上一個頁面的渲染狀態(tài) - 設(shè)置上一個/當(dāng)前頁面的動效以配合效果
-
對于 card
脂倦、modal
比較好理解,很容易適配到具體使用場景元莫,transparentModal
值則更傾向于模擬彈窗效果赖阻,比如
<Stack.Navigator>
<Stack.Screen name="Home" component={HomeStack} />
<Stack.Screen
name="Modal"
component={ModalScreen} // ModalScreen 為彈窗組件
options={{
presentation: 'transparentModal',
headerShown: false, // 不要顯示 Header
cardOverlayEnabled: true, // 彈窗下顯示一個半透明 overlay 蒙層
}}
/>
</Stack.Navigator>
如果需要對于 ModalScreen
自定義動畫效果,可以借助 useCardAnimation
接口實現(xiàn)
import { Animated, View, Text, Pressable, Button, StyleSheet } from 'react-native';
import { useTheme } from '@react-navigation/native';
import { useCardAnimation } from '@react-navigation/stack';
function ModalScreen({ navigation }) {
const { colors } = useTheme();
const { current } = useCardAnimation();
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center'}}>
<Pressable
style={[StyleSheet.absoluteFill, {backgroundColor: 'rgba(0, 0, 0, 0.5)' } ]}
onPress={navigation.goBack}
/>
<Animated.View
style={{
padding: 16, width: '90%', maxWidth: 400, borderRadius: 3,
backgroundColor: colors.card,
transform: [
{
scale: current.progress.interpolate({
inputRange: [0, 1],
outputRange: [0.9, 1],
extrapolate: 'clamp',
}),
},
],
}}
>
<Text>
Mise en place is a French term that literally means “put in place.” It
also refers to a way cooks in professional kitchens and restaurants
set up their work stations—first by gathering all ingredients for a
recipes, partially preparing them (like measuring out and chopping),
and setting them all near each other. Setting up mise en place before
cooking is another top tip for home cooks, as it seriously helps with
organization. It’ll pretty much guarantee you never forget to add an
ingredient and save you time from running back and forth from the
pantry ten times.
</Text>
<Button
title="Okay" color={colors.primary} style={{ alignSelf: 'flex-end' }}
onPress={navigation.goBack}
/>
</Animated.View>
</View>
);
}
六踱蠢、BottomTabsNavigator.Screen options
最基本屬性
-
title
: 設(shè)置為 string, 會作為標(biāo)題文字headerTitle
的 fallback火欧,底部 Tab 的tabBarLabel
文字的 fallback -
lazy
: 選修卡頁面是否為懶加載(即切換至頁面時才渲染),默認(rèn)為true
-
unmountOnBlur
: 頁面失去焦點后是否自動卸載茎截,若為true
苇侵,每次切換至頁面都會重新加載,默認(rèn)為false
與 Header 組件相關(guān)的屬性
參考 StackNavigator options
中與 Header 組件相關(guān)的屬性企锌,支持 Elements/Header
組件直接支持的所有屬性榆浓,參考上面 StackNavigator.Screen options
所介紹的不含 *
的 Header 相關(guān)屬性。除了這些屬性外撕攒,額外擴(kuò)展并支持以下屬性
- *
header
: 自定義標(biāo)題欄組件陡鹃,定義為函數(shù),返回一個 RN 組件抖坪;設(shè)置該屬性杉适,即不再使用默認(rèn) Header。 - *
headerShown
: 是否顯示標(biāo)題欄
與 TabBar 組件相關(guān)的屬性
這些屬性在 V5 版時柳击,是設(shè)置在 Navigator
的 tabBarOptions
屬性中,V6 版移動到了 Screen
的 options
中片习,為了更容易理解捌肴,可結(jié)合下圖
上圖中除了 sceneContainerStyle
/ tabBar
是在 BottomTabsNavigator.Navigator
屬性中設(shè)置的,其他都是在 BottomTabsNavigator.Screen
中設(shè)置的藕咏。默認(rèn)的 tabBar
組件會利用下面要介紹的 Screen options
渲染為上圖結(jié)構(gòu)状知,如果自定義了 tabBar
組件,則可利用 Screen options
自行設(shè)計結(jié)構(gòu)孽查。既然提到了 Navigator
屬性饥悴,順帶說下另外兩個支持的屬性:
-
detachInactiveScreens
: 切換 Tab 后,是否回收未顯示 Tab 頁面內(nèi)存盲再,默認(rèn)為true
-
backBehavior
: 在 Tab 頁面按下物理(虛擬)返回鍵后的行為西设,支持以下值-
firstRoute
: 跳轉(zhuǎn)到第一個 Tab 頁面(默認(rèn)) -
initialRoute
: 跳轉(zhuǎn)到載入時的 Tab 頁面(由initialRouteName
指定的頁面) -
order
: 按照順序依次跳轉(zhuǎn)到前一個頁面 -
history
: 按照瀏覽歷史依次跳轉(zhuǎn)到上一個訪問的頁面 -
none
: 什么都不做,通常會直接返回桌面
-
說完 BottomTabsNavigator.Navigator
的屬性答朋,下面說一下 BottomTabsNavigator.Screen
的 options
屬性中與 TabBar
相關(guān)的屬性贷揽,可結(jié)合上圖進(jìn)行理解。
-
tabBarBackground
: 默認(rèn)情況下梦碗,背景為tabBar
的背景色禽绪,若指定了該組件蓖救,tabBar
背景色會自動設(shè)置為透明,tabBarBackground
組件在 Z 軸上位于tabBar
的下面印屁,可以設(shè)置一些個性的 UI 效果循捺,比如漸變色、圖片雄人、毛玻璃等从橘。 -
tabBarStyle
: TabBar 整體容器的樣式 -
tabBarShowLabel
: 是否顯示 TabBar 的文字 -
tabBarLabelPosition
: TabBar 文字顯示的位置,默認(rèn)會根據(jù)設(shè)備類型自動顯示-
below-icon
: 文字顯示在圖標(biāo)下面(手機(jī)默認(rèn)) -
beside-icon
: 文字顯示在圖標(biāo)右邊(平板默認(rèn))
-
-
tabBarInactiveTintColor
: 默認(rèn)狀態(tài)下文字顏色 -
tabBarActiveTintColor
: 激活狀態(tài)下文字顏色 -
tabBarInactiveBackgroundColor
: 默認(rèn)狀態(tài)下背景顏色 -
tabBarActiveBackgroundColor
: 激活狀態(tài)下背景顏色 -
tabBarHideOnKeyboard
: 在鍵盤展開時隱藏 TabBar柠衍,默認(rèn)false
對于 StackNavigator
洋满,每個頁面都是獨立的,所有屬性都是對于所屬頁面而言的珍坊。而 BottomTabsNavigator
則不然牺勾,多個頁面公用同一個 TabBar,以上是共用屬性阵漏,每個處于激活的頁面設(shè)置的屬性都會影響整個 TabBar驻民,比如下面這種效果
上面為共用屬性,而以下屬性則是每個頁面的私有屬性履怯,即僅會影響所屬頁面的 TabItem回还。
-
tabBarItemStyle
: TabBar Item 容器的樣式 -
tabBarButton
: 設(shè)置tabBarLabel
,tabBarIcon
,tabBarBadge
的容器組件,通常無需設(shè)置叹洲,可參考默認(rèn)的 button -
tabBarIcon
: TabBar 圖標(biāo)組件(會收到{ focused: boolean, color: string, size: number }
參數(shù)) -
tabBarIconStyle
: TabBar 圖標(biāo)樣式 -
tabBarBadge
: TabBar 角標(biāo)柠硕,可以是String
或Number
-
tabBarBadgeStyle
: TabBar 角標(biāo)樣式 -
tabBarLabel
: TabBar 要顯示的文字(不設(shè)置會使用title
屬性),可以設(shè)置為String
或返回 React 組件的函數(shù)(函數(shù)會收到{ focused: boolean, color: string }
參數(shù)) -
tabBarLabelStyle
: TabBar 文字的樣式 -
tabBarAllowFontScaling
: TabBar 文字是否隨系統(tǒng)字體大小縮放 -
tabBarAccessibilityLabel
: 無障礙標(biāo)簽 -
tabBarTestID
: 用于本地測試的 ID
結(jié)合 【四运提、頁面內(nèi)】 章節(jié)蝗柔,可以使用 React.useLayoutEffect
在頁面內(nèi)設(shè)置 options
,僅適合 TabBar 的共用屬性民泵,而不適合 TabBar 私有屬性癣丧,畢竟不能讓用戶激活了 Tab 頁面后,才能看到諸如 tabBarBadge
/ tabBarLabel
信息栈妆,這一點需要注意胁编。
七、接口
1. 頁面組件會收到 navigation 和 route 兩個參數(shù)鳞尔。
navigation
提供相關(guān)操作API嬉橙,根據(jù) Screen 組件所在導(dǎo)航器的不同,API 也會有所不同寥假。
通用API憎夷,所有類型導(dǎo)航器都可使用
-
navigate
: 跳轉(zhuǎn)到指定頁面 -
goBack
: 關(guān)閉當(dāng)前頁面返回到上一頁 -
reset
: 重置導(dǎo)航器狀態(tài) -
setParams
: 更新當(dāng)前頁面的route.params
參數(shù) -
setOptions
: 更新當(dāng)前頁面的options
選項配置 -
isFocused
: 檢測當(dāng)前頁面是否處于活動狀態(tài) -
dispatch
: 發(fā)送 Action 給導(dǎo)航器,可參考 文檔 -
getParent
: 若當(dāng)前導(dǎo)航器嵌套在另外一個導(dǎo)航器中昧旨,返回上級導(dǎo)航器拾给,否則返回undefined
-
getState
: 獲取導(dǎo)航器當(dāng)前的狀態(tài)祥得,一般用不到,少數(shù)情況下可能用得到
stack 導(dǎo)航器獨有
-
replace
: 替換當(dāng)前頁面為指定頁 -
push
: 添加一個新頁面到堆棧 -
pop
: 從堆棧彈出當(dāng)頁面 -
popToTop
: 返回到堆棧的起始頁
tab 導(dǎo)航器獨有
-
jumpTo
: 跳轉(zhuǎn)到 Tab 內(nèi)的指定頁面
route
屬性提供當(dāng)前頁面的相關(guān)信息
-
key
: 頁面唯一值蒋得,通常為自動生成 -
name
: 所定義的頁面名稱 -
path
: 頁面路徑级及,通過 Link 打開的頁面才會有這個屬性 -
params
: 頁面導(dǎo)航時傳遞的參數(shù)
2. 非頁面組件如何使用 navigation 和 route 屬性
通常可以在頁面內(nèi)調(diào)用組件時额衙,將 navigation 和 route 以 props 的方式傳遞給子組件虹菲,但這樣對于嵌套較深的組件使用起來非常痛苦恩商,另外子組件也要依賴父組件正確傳遞,React Navigation
提供了另外一種方法:
import * as React from 'react';
import { View, Text, Button } from 'react-native';
import { useNavigation, useRoute } from '@react-navigation/native';
function MyConmpoent() {
const navigation = useNavigation();
const route = useRoute();
return <View>
<Text>{route.params.caption}</Text>
<Button
title="Back"
onPress={() => {
navigation.goBack();
}}
/>
</View>;
}
// 對于 class 組件
class MyConmpoent extends React.Component {
render() {
const { navigation, route } = this.props;
return <View>
<Text>{route.params.caption}</Text>
<Button
title="Back"
onPress={() => {
navigation.goBack();
}}
/>
</View>;
}
}
// Wrap and export
export default function(props) {
props.navigation = useNavigation();
props.route = useRoute();
return <MyConmpoent {...props} />;
}
3. 其他可用的 Hook API
import * as React from 'react';
import { View, Text, Button } from 'react-native';
// 可用 Hook API
import {
useNavigation,
useIsFocused,
useLinkTo,
useLinkProps,
useLinkBuilder,
useScrollToTop,
useTheme
} from '@react-navigation/native';
// function 組件
function MyConmpoent() {
const theme = useTheme();
const isFocused = useIsFocused();
const state = useNavigationState(state => state);
const linkTo = useLinkTo();
const { onPress, ...props } = useLinkProps({ to, action });
const buildLink = useLinkBuilder();
const scrollRef = React.useRef(null);
useScrollToTop(scrollRef);
// code
}
// 對于 class 組件
class MyConmpoent extends React.Component {
render() {
const {theme, isFocused, state, linkTo, onPress, buildLink, scrollRef} = this.props;
// code
}
}
// Wrap and export
export default function(props) {
props.theme = useTheme();
props.isFocused = useIsFocused();
props.state = useNavigationState(state => state);
props.linkTo = useLinkTo();
props.onPress = useLinkProps({ to, action }).onPress;
props.buildLink = useLinkBuilder();
const scrollRef = React.useRef(null);
useScrollToTop(scrollRef);
return <MyConmpoent {...props} />;
}
將這些API分為三類,第一類有 useTheme
, isFocused
, useNavigationState
响谓,這三個使用 get 型 API 是可以直接獲取的撞蚕,比如 navigation.isFocused()
笛钝,但在 render() 界面時依賴相關(guān)變量的話艰猬,這些 API Hook 就比較有用了,當(dāng)這些相關(guān)變量發(fā)生變化斧账,界面會自動更新谴返。
第二類為 useLinkTo
, useLinkProps
, useLinkBuilder
,這三個都與 Link
功能有關(guān)咧织,以偽代碼做個說明:
import { Link, useLinkTo, useLinkProps, useLinkBuilder} from '@react-navigation/native';
// Link 組件使用 Text 模擬嗓袱,類似于 Html 的 a 標(biāo)簽,接受 to / action 兩個參數(shù)
// to 指定目標(biāo)頁面习绢, action 與 navigate.dispatch 接口參數(shù)同渠抹,不指定為 navigate action
function Componet() {
return (
<Link
to={{ screen: 'Profile', params: { id: 'jane' } }}
action={StackActions.replace('Profile', { id: 'jane' })}
> Go </Link>
);
}
// 可以使用 useLinkBuilder 生成 Link 組件的 to 參數(shù)
function Componet({ route }) {
const buildLink = useLinkBuilder();
return (
<Link
to={buildLink(route.name, route.params)}
action={StackActions.replace('Profile', { id: 'jane' })}
> Go </Link>
);
}
// Link 組件使用 Text 模擬,可以使用 useLinkProps 自定義其他組件模擬
function LinkButton({ route }) {
const { onPress, ...props } = useLinkProps({ to, action });
return (
<Button onPress={onPress}> Go </Button>
);
}
// 這樣就可以和使用 Link 一樣的方式闪萄,使用自己創(chuàng)建的 'Link' 組件了
<LinkButton to={} action={}/>
// useLinkTo 與以上不同梧却,更類似于 navigation.navigate , 用于跳轉(zhuǎn)到指定頁面
// 但提供的參數(shù)不同,這里需要提供 Deep Link 所設(shè)置的頁面 path
function Screen() {
const linkTo = useLinkTo();
return (
<Button onPress={() => linkTo('/profile/jane')}>
Go to Jane's profile
</Button>
);
}
第三類是 useScrollToTop
Hook桃煎,該 API 的作用是為了模擬原生 Bottom Tab 的效果,如果 Tab 頁面是可滾動的(比如 ScrollView
大刊,FlatList
)为迈,在頁面已處于激活狀態(tài)的情況下,點擊底部 Tab 圖標(biāo)缺菌,頁面滾動到最頂部葫辐。
import * as React from 'react';
import { ScrollView } from 'react-native';
import { useScrollToTop } from '@react-navigation/native';
function Screen() {
const ref = React.useRef(null);
useScrollToTop(ref);
// 如果希望點擊底部 Tab 圖標(biāo)不是滾動到最頂部,可以這樣來指定一個 offset 值
// useScrollToTop(React.useRef({
// scrollToTop: () => ref.current?.scrollToOffset({ offset: -100 }),
// }));
return <ScrollView ref={ref}>{/* content */}</ScrollView>;
}
最后伴郁,除了以上 Hook API耿战,React Navigation
還提供了一個 useFocusEffect
Hook,該 API 與以上都不同焊傅,所以放到最后單獨說一下剂陡。以上 API 都是返回值式的 Hook狈涮,該 Hook 則更類似于添加一個 listener 監(jiān)聽:
import { useFocusEffect } from '@react-navigation/native';
function Profile({ userId }) {
const [user, setUser] = React.useState(null);
// useFocusEffect 與 React.useEffect 類似,不同之處在于只會在頁面激活時觸發(fā)
// 可使用 React.useCallback 包裹回調(diào)鸭栖,這樣回調(diào)只會在首次激活或依賴項發(fā)生變化才觸發(fā)
// 否則每次頁面激活都會被觸發(fā)
useFocusEffect(
React.useCallback(() => {
const unsubscribe = API.subscribe(userId, user => setUser(user));
return () => unsubscribe();
}, [userId])
);
// 一般遠(yuǎn)程請求都是異步的歌馍,所以務(wù)必只請求一次
//(因為該回調(diào)不一定僅觸發(fā)一次,可能造成競爭請求)
// 如果請求 API 未提供取消機(jī)制晕鹊,需自行處理松却,如:
useFocusEffect(
React.useCallback(() => {
let isActive = true;
const fetchUser = async () => {
try {
const user = await API.fetch({ userId });
if (isActive) {
setUser(user);
}
} catch (e) {
// Handle error
}
};
fetchUser();
return () => {
isActive = false;
};
}, [userId])
);
return <ProfileContent user={user} />;
}
// 對于 class 組件,需采用類似于 StatusBar 的方法
function FetchUserData() {
useFocusEffect(
....
);
return null;
}
class Profile extends React.Component {
_handleUpdate = user => {
// Do something with user object
};
render() {
return (
<>
<FetchUserData />
{/* 其他組件 */}
</>
);
}
}