前一篇在 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 很多傳參數的疑惑都可以瞬間豁然開朗,是個非常好的投資。