iT邦幫忙

2017 iT 邦幫忙鐵人賽
DAY 25
1
Modern Web

使用 Modern Web 技術來打造 Native App系列 第 25

Day 25:實作 Navigation 與 Drawer

前言

前一篇在 App 上呈現 Github User 頁面,必須使用 GraphQL Query,這篇想要來嘗試實作一個使用 GraphQL Mutation 的功能 - 在 Github Repo 開新 Issue。

但是悲劇了,Github 的 GraphQL Mutation 目前都只實作了 Project 跟 Pull Request Review 相關的功能:

這兩個系列功能有點太複雜而且筆者不想把它們放進這個 App,所以這篇決定改成實作 Navigation 與 Drawer。

Navigation 的抉擇

這系列前面已經有了兩篇 Navigation 相關的介紹:

因為想在不久之後 react-navigation 出現時換過去,而且希望有個立即可用的 Drawer (側邊欄),所以筆者選擇用 ExNavigation 進行開發。

安裝 ExNavigation

因為它的 Source Code 裡面有用到一些 @withNavigation 這樣的 decorator,所以必須多安裝一個開發時的依賴套件 babel-preset-react-native-stage-0

npm install --save @exponent/ex-navigation 
npm install --save-dev babel-preset-react-native-stage-0

或是用 Yarn:

yarn add @exponent/ex-navigation 
yarn add --dev babel-preset-react-native-stage-0

還要再 .babelrc 加上 react-native-stage-0/decorator-support 這個 preset

{
  "presets": ["react-native-stage-0/decorator-support"]
}

建立 Router

使用 ExNavigation 時,必須設定特定的 Route Key 對應到特定的 Component。雖然好多的 Thunk 有點複雜,不過寫起來是這樣子:

/**
 * @flow
 */
import {
  createRouter,
} from '@exponent/ex-navigation';

import HomeScreen from './HomeScreen';
import UserScreen from './UserScreen';
import RepoScreen from './RepoScreen';


const router = createRouter(() => ({ // <- Function
  home: () => HomeScreen, // <- 也是 Function
  user: () => UserScreen,
  repo: () => RepoScreen,
}));


export default router;

總之把所有畫面的 Component 都設定一個 Key 就對了。

放置 Navigation Context Provider

前幾天才看到這則 Tweet:

自從 Context 出現後,又或是 Redux 開始使用之後,因為太方便各式各樣的 Provider 開始各行其道,實驗性質的 API 不知不覺大家都用得很開心了。雖不知道未來會怎麼走向,不過還是就給他多加一層 NavigationProvider,並放進去剛剛完成的 router

import {
  NavigationProvider,
} from '@exponent/ex-navigation';

import router from './router';
import Main from './Main';

export default class LoggedIn extends Component {
  render() {
    return (
      <ApolloProvider client={client}>
        <NavigationProvider router={router}>
          <Main />
        </NavigationProvider>
      </ApolloProvider>
    );
  }
}

如此一來就可以在 Main 裡面輕鬆的處理 Navigation。

LoggedIn 是上一篇所完成的其中一個 Component,而 Main 則是接下來要完成的 Component。

設定 DrawerNavigation

首先,讓 Main Render 一個 DrawerNavigation

import React, { Component } from 'react';
import {
  DrawerNavigation,
} from '@exponent/ex-navigation';

export default class Main extends Component {
  render() {
    return (
      <DrawerNavigation
        drawerPosition="left"
        drawerWidth={200}
        initialItem="home"
        drawerStyle={{ marginTop: 20 }}
      >
      </DrawerNavigation>
    );
  }
}  
  • drawerPosition:
    left - 側邊欄出現在左邊
  • drawerWidth:200 - 寬度為 200
  • initialItem:home - 設定初始畫面是 Focus 哪個 DrawerNavigationItem
  • drawerStyle:可以調整 Drawer 的樣式

DrawerNavigation 裡面 必須傳遞一組組的 DrawerNavigationItemStackNavigation 當作 Children:

<DrawerNavigationItem
  id="home"
  selectedStyle={styles.selectedItemStyle}
  renderTitle={isSelected => this.renderTitle('Home', isSelected)}
>
  <StackNavigation
    id="home"
    initialRoute={router.getRoute('home')}
  />
</DrawerNavigationItem>

DrawerNavigationItem 是在設定側邊欄,selectedStyle 是被選到時的樣式,renderTitle 則必須回傳要顯示的 Element,這邊實作是用下面這個 Function,回傳一個 Text Element 並在被選擇時加上不同的樣式:

renderTitle = (text: string, isSelected: bool) => (
  <Text style={[
    styles.buttonTitleText, 
    isSelected ? styles.selectedText : null,
  ]}>
    {text}
  </Text>
);

NavigationItem 弄完長這個樣子:

剩下的就是用 StackNavigationinitialRoute 來指定被選到時一開始要顯示 Component。

這邊會用到前面定義好的 router 還有對應的 Key:

router.getRoute('home')

Component 如果有其他的 Props 要傳,就需要用第二個參數傳進去:

router.getRoute('repo', {
  owner: 'chentsulin',
  name: 'IronGithub',
})

設定好後看起來大致像這樣:


另外,每個畫面的 navigationBar 都是可以調的,一種做法是在 Component 裡面定義:

class UserScreen extends Component {
  static route = {
    navigationBar: {
      visible: false,
    },
  };

  //...
}

這邊把 visible 調成 false,所以在 User 的畫面是看不到 navigationBar 的,但還是可以拉出側邊選單,實際狀況如下:

Repo 頁面也簡單做了一下,不過篇幅有限,再下去讀者都要不耐煩了,這邊就不再贅述。

結語

這邊所提到的一些 API 都只能算是冰山一角,而在寫 React Native 的 Navigation (也包括 ExNavigation) 時,常常會有文件不足而且範例又只有一個的狀況。

這時候如果能擁有強大的原始碼探勘技術其實能有很大的幫助,打看 Repo 的 src 資料夾直接找對應的 API 或 Component,或是打開 node_modules 直接去加 console.log 來研究,都是最有效率的方式,畢竟 Stackoverflow 上可能很難找到很有幫助的答案。

這邊也推薦讀者花一點時間學習 Flow,通常看了 Flow 的 Type Definition 很多傳參數的疑惑都可以瞬間豁然開朗,是個非常好的投資。


上一篇
Day 24:在 App 上呈現 Github User 頁面
下一篇
Day 26:在 React Native 進行單元測試
系列文
使用 Modern Web 技術來打造 Native App30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言