前一篇在 App 上呈現 Github User 頁面,必須使用 GraphQL Query,這篇想要來嘗試實作一個使用 GraphQL Mutation 的功能 - 在 Github Repo 開新 Issue。
但是悲劇了,Github 的 GraphQL Mutation 目前都只實作了 Project 跟 Pull Request Review 相關的功能:

這兩個系列功能有點太複雜而且筆者不想把它們放進這個 App,所以這篇決定改成實作 Navigation 與 Drawer。
這系列前面已經有了兩篇 Navigation 相關的介紹:
因為想在不久之後 react-navigation 出現時換過去,而且希望有個立即可用的 Drawer (側邊欄),所以筆者選擇用 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"]
}
使用 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 就對了。
前幾天才看到這則 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。
首先,讓 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>
);
}
}
left - 側邊欄出現在左邊200 - 寬度為 200home - 設定初始畫面是 Focus 哪個 DrawerNavigationItem
DrawerNavigation 裡面 必須傳遞一組組的 DrawerNavigationItem 和 StackNavigation 當作 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 弄完長這個樣子:

剩下的就是用 StackNavigation 的 initialRoute 來指定被選到時一開始要顯示 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 很多傳參數的疑惑都可以瞬間豁然開朗,是個非常好的投資。