React Navigation V5/V6 用法詳解

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

安裝完之后赖临,在 js 入口文件,如 index.js 頂部添加 import 'react-native-gesture-handler';灾锯,少了這一句兢榨,可能會導(dǎo)致生產(chǎn)環(huán)境 app 出現(xiàn)閃退現(xiàn)象。

其他:官方提供的幾種導(dǎo)航器顺饮,根據(jù)需要安裝吵聪,也可以參考自行建構(gòu)

  • Draweryarn add @react-navigation/drawer react-native-gesture-handler react-native-reanimated
  • Bottom Tabsyarn add @react-navigation/bottom-tabs
  • Material Bottom Tabsyarn add @react-navigation/material-bottom-tabs react-native-paper react-native-vector-icons
  • Material Top Tabsyarn add @react-navigation/material-top-tabs react-native-tab-view react-native-pager-view

安裝所需導(dǎo)航器并安裝相應(yīng)依賴,有些依賴可能有重復(fù)兼雄,安裝一次就行了吟逝,比如 DrawerStack 都依賴 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>);
  1. 導(dǎo)航器總是使用 <Nav.Navigator> <Nav.Screen/> </Nav.Navigator> 格式組合頁面佃乘,其中 Nav 可以使用官方提供的幾個導(dǎo)航器 stack囱井、drawerbottom-tabs 趣避、material-bottom-tabs庞呕、material-top-tabs,當(dāng)然也可參考自行建構(gòu)程帕。
  2. 導(dǎo)航器本身可以作為另外一個導(dǎo)航器的 Screen component住练,即當(dāng)作一個普通頁面作為另外一個導(dǎo)航器的子頁面。
  3. 最終使用 NavigationContainer 包裹最頂層導(dǎo)航器愁拭。
    一般使用 Stack 作為頂層導(dǎo)航器(如上的例子)讲逛,在 Tab 內(nèi)打開頁面會覆蓋整個屏幕,退回后才能進(jìn)行 Tab 切換岭埠。
    當(dāng)然也可以使用 Tab 作為頂層導(dǎo)航器妆绞,切換頁面時 TabBar 不會被覆蓋,每個 Tab 都有獨立的堆棧枫攀。

React NavigationNavigationContainerNav.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 傳遞,比如上面的示例蝴悉,childrenStack.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è)置的 screenOptionsscreen.options 屬性完全相同晃财,作為所有 screen 的 options 默認(rèn)值叨橱。

閱讀 @react-navigation/core 源碼和文檔,這個參數(shù)的值可以是 ObjectFunction({route, navigation}) => Object断盛,且未對 Object 字段做任何限制罗洗,而只是為導(dǎo)航器的實現(xiàn)提供了一個頂層 API,比如官方的兩個實現(xiàn)支持不同的 options:

// 直接設(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)航器還有特有消息贩绕,如:

  • @react-navigation/stackevents
  • @react-navigation/bottom-tabsevents火的;

該屬性的值與 screenOptions 有點類似,也可以指定為 ObjectFunction淑倾,如

// 直接設(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)湃累,下面介紹 stackoptions 會提到目前的變化。

6. @react-navigation/bottom-tabs

detachInactiveScreens / backBehavior (繼承自 @react-navigation/routers) / sceneContainerStyle / tabBar / lazy / tabBarOptions (V6版這兩個屬性移動到了 options 中配置) 屬性(文檔

三迂尝、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.Navigatormode 屬性轉(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显晶、HeaderTitleHeaderBackButton壹士、MissingIcon磷雇、PlatformPressableResourceSavingView 組件和一些工具函數(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版名稱: headerPressColorAndroid): 點擊 Header 內(nèi)按鈕組件的水波紋顏色胧洒,僅對 Android 5 及以上
  • +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)移到了這里)
  • safeAreaInsets: 安全區(qū)域設(shè)置(針對劉海屏機(jī)型)诗越,默認(rèn)情況下會自動設(shè)置,但可以通過該屬性通過 {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攘滩,Androidpresentation="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}民逼;比如手勢方向 gestureDirectionhorizontal,那么只有在左邊緣 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.Screenoptions 屬性支持的所有配置占调,最后再對影響頁面效果較大的 headerModepresentation 配置稍作說明移剪,headerMode 支持的兩個值:

  • float: 此時頁面 Header 與頁面 Card 是分離的究珊,有一個 Header 容器組件總是在頂部,所有頁面的 Header 都在這個容器里纵苛,這種模式下剿涮,在切換頁面時, Header 與 Card 可以獨立執(zhí)行各自的切換動效攻人,比如模擬 iOS 原生效果取试。
  • screen: 每個頁面的 Header 都在各自的 Card 頂部,即每個頁面整體獨立怀吻。切換頁面時瞬浓,是整個頁面進(jìn)行動效過渡。
  • none: 該模式在 V6 版已移除蓬坡,使用 headerShown=false 替代

presentation 配置更像一個快捷方式猿棉,修改該值,可能會自動設(shè)置 cardOverlayEnabled屑咳、detachPreviousScreen萨赁、headerModegestureDirection兆龙、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è)置在 NavigatortabBarOptions 屬性中,V6 版移動到了 Screenoptions 中片习,為了更容易理解捌肴,可結(jié)合下圖

BottonTab(紅色為支持屬性)

上圖中除了 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.Screenoptions 屬性中與 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驻民,比如下面這種效果

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)柠硕,可以是 StringNumber
  • 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. 頁面組件會收到 navigationroute 兩個參數(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 />
        {/* 其他組件 */}
      </>
    );
  }
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末溅话,一起剝皮案震驚了整個濱河市晓锻,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌飞几,老刑警劉巖砚哆,帶你破解...
    沈念sama閱讀 212,657評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異循狰,居然都是意外死亡窟社,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,662評論 3 385
  • 文/潘曉璐 我一進(jìn)店門绪钥,熙熙樓的掌柜王于貴愁眉苦臉地迎上來灿里,“玉大人,你說我怎么就攤上這事程腹∠坏酰” “怎么了?”我有些...
    開封第一講書人閱讀 158,143評論 0 348
  • 文/不壞的土叔 我叫張陵寸潦,是天一觀的道長色鸳。 經(jīng)常有香客問我,道長见转,這世上最難降的妖魔是什么命雀? 我笑而不...
    開封第一講書人閱讀 56,732評論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮斩箫,結(jié)果婚禮上吏砂,老公的妹妹穿的比我還像新娘。我一直安慰自己乘客,他們只是感情好狐血,可當(dāng)我...
    茶點故事閱讀 65,837評論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著易核,像睡著了一般匈织。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 50,036評論 1 291
  • 那天缀匕,我揣著相機(jī)與錄音纳决,去河邊找鬼。 笑死弦追,一個胖子當(dāng)著我的面吹牛岳链,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播劲件,決...
    沈念sama閱讀 39,126評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼掸哑,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了零远?” 一聲冷哼從身側(cè)響起苗分,我...
    開封第一講書人閱讀 37,868評論 0 268
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎牵辣,沒想到半個月后摔癣,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,315評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡纬向,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,641評論 2 327
  • 正文 我和宋清朗相戀三年择浊,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片逾条。...
    茶點故事閱讀 38,773評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡琢岩,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出师脂,到底是詐尸還是另有隱情担孔,我是刑警寧澤,帶...
    沈念sama閱讀 34,470評論 4 333
  • 正文 年R本政府宣布吃警,位于F島的核電站糕篇,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏酌心。R本人自食惡果不足惜拌消,卻給世界環(huán)境...
    茶點故事閱讀 40,126評論 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望安券。 院中可真熱鬧墩崩,春花似錦、人聲如沸完疫。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,859評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽壳鹤。三九已至,卻和暖如春饰迹,著一層夾襖步出監(jiān)牢的瞬間芳誓,已是汗流浹背余舶。 一陣腳步聲響...
    開封第一講書人閱讀 32,095評論 1 267
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留锹淌,地道東北人匿值。 一個月前我還...
    沈念sama閱讀 46,584評論 2 362
  • 正文 我出身青樓,卻偏偏與公主長得像赂摆,于是被迫代替她去往敵國和親挟憔。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,676評論 2 351

推薦閱讀更多精彩內(nèi)容