[0寫App基礎]第一個React Native App與CRNA(Expo)實作心得
前陣子在實習其間首次開發React Native App,以CRNA(Create React Native App)配搭Expo實作。藉著這個機會來作個記錄和心得吧﹗嚴格來說,我以往沒有開發過app的經驗,甚至連java script也未寫過,所以可以說是由0開始的React Native App吧。
萬事起頭難,因此我將會分享如何可以有系統地,0寫app基礎的開始寫react native app。
將會用到的工具:
Expo
ignite CLI
NativeBase
作為新手,總會遇到以下的問題:架構怎麼擺,檔案怎麼放,網上找到的code應該放哪裡……因此,有一個好的模板的話,你會不用費心於架構,甚至可以大概預計到網上找的code應該放哪裡。因此,我以下將會用到一個叫做ignite CLI的模板去寫我的app。
首先先介紹一下Expo,Expo是一個工具幫你搞定了所有除了java script以外的問題。以最簡單來說,就是只要java script沒寫錯,app就能順利的build出來,不會遇到其他問題。另外,Expo 把統合了一堆實用的API,令你可以簡單的用到native modules或其他實用的東西, 比如說default camera, map, browser, etc。
當然使用expo也有缺點,目前我認為最大的缺點就是不能使用非純java script的library。如果要安裝的library不是純java script寫的話,就需要detach expo,即不再用expo寫你的project。那你要build app的時候就會變得有點麻煩。
然後要介紹的是ignite CLI。

ignite CLI 是一個模板,將你的project做好結構,分好了component, container(即是page), navigation, etc要放的位置,令你寫起來不用煩惱。同時,ignite CLI 也配備了一些plugin,例如用於處理不同國家語言的i18n,令你更方便。
Notes:
簡單來說,Components是會重用的UI組件,Containers是pages,Navigation是app的導航結構,Services是API,Themes是字型大小顏色格式類東西
安裝說明:
安裝時請一步一步確認沒有問題才進行下一步
首先要按指引用command裝了expo。
https://expo.io/learn
Expo官網這部分的指令好像寫錯了,expo create project 的指令應是這樣
exp init my-new-projectcd my-new-projectexp start 或 exp r
如果不想用command prompt的話,可以使用Expo XDE,Expo XDE 提供了介面去create project, open in device, publish, etc. 有興趣可以去看一看,不會在這裡詳說。

由於我是用android進行開發,所以可以用自己的手機或Genymotion Android Emulator進行測試。不論用了哪個方法甚麼,在連接好android手機後,可以用以下指令在android開啟app進行測試。我自己是用Genymotion的。
如果用了exp r這個指令去start的話,那一個command prompt將不可以運行exp android,我會開多一個comand prompt
cd my-new-project
exp android完成後,之後就要裝ignite CLI了。
https://github.com/infinitered/ignite
npm install -g ignite-cli由於我將會用expo進行開發,因此不能直接用ignite CLI的new指令create project,我會用一個經過修改,能在expo運行的ignite CLI: Ignite Expo Boilerplate。這個boilerplate需要先安裝ignite CLI 才能使用。使用以下指令create project with ignite CLI
https://github.com/jbosse/ignite-expo-boilerplate
ignite new MyLatestCreation -b ignite-expo然後可以測試
cd MyLatestCreation
exp start 或 exp r這時候可能會遇到error,如果遇到這個情況,就請按指引裝error要求的東西,比如說npm。如遇到這情況可以輸入以下指令。
npm install如果沒問題,就可以進行測試。在手機開啟後,可能會遇到以下error

這個問題是由於expo sdkVersion 與 React Native Version不對所做成。Native version所用的0.52.0是對26.0.0的。
詳情請參考 https://docs.expo.io/versions/v26.0.0/sdk/
因此可以透過修改app.json解決。由原本的
"expo": {
...
"sdkVersion": "25.0.0",
...
}改成
"expo": {
...
"sdkVersion": "26.0.0",
...
}然後restart一次整個project,如果看到這個畫面就代表成功使用ignite CLI create project了。

現在,就可以開始快樂愉快地寫code了!
如果你想快速做好漂亮UI,推薦使用NativeBase這個套件。
NativeBase是一個套件提供一些已經做好的component給你使用,可以令你快速寫好漂亮的UI。
https://docs.nativebase.io/docs/GetStarted.html
請按指引按裝。
npm install native-base --save注意,由於我使用Expo進行開發,因此要看Setup with CRNA
npm install @expo/vector-icons --save NativeBase 會用到一些custom字體,而這些字體需要在Visual component render前load完,所以要在Root Container加上以下的code。如何要用到Icon也是同理,需要先在render前load完。
async componentWillMount() {
await Expo.Font.loadAsync({
'Roboto': require('native-base/Fonts/Roboto.ttf'),
'Roboto_medium': require('native-base/Fonts/Roboto_medium.ttf'),
'Ionicons': require('native-base/Fonts/Ionicons.ttf'),
});
)
}去到這裡,大家可以參考這篇看看react native component 的life cycle,看看componentWillMount這個function是做甚麼的。
由於要確保NativeBase用到的custom字體要load完才render,需要一個state去確保load完。所以整個class大概是這樣。
class RootContainer extends Component {
state={
isReady:false,
}
componentDidMount () {
this.props.startup()
}
async componentWillMount() {
await Expo.Font.loadAsync({
'Roboto': require('native-base/Fonts/Roboto.ttf'),
'Roboto_medium': require('native-base/Fonts/Roboto_medium.ttf'),
'Ionicons': require('native-base/Fonts/Ionicons.ttf'),
});
this.setState({isReady:true})
}
render () {
if (this.state.isReady) {
return (
<View style={styles.applicationView}>
<StatusBar barStyle='light-content'/>
<ReduxNavigation/>
</View>
)
}
else
return <AppLoading/>
}
}<AppLoading/> 是一個顯示空白的React component。
如果想簡單了解下React native 的基本概念,可以參下以下網址,雖然我不是用他的boilerplate。
https://noootown.gitbooks.io/deeperience-react-native-boilerplate/content/Introduction/React%20Native%20&%20Boilerplate.html
現在我簡單介紹一下Ignite Expo Boilerplate的運行次序
首先會從index.js開始,可是這個我不太清楚,所以本文將會忽略這部分。然後app.js會是index.js load完後要load的第一個component。然後會變成這樣:
app.js -> RootContainer.js(第一個visual component) -> ReduxNavigaion -> AppNavigation.js ->你的第一頁
因此我現在直接把前面的東西都忽略,直接看看AppNavigation.js

AppNavigation指的是app的導航,跳轉頁面的結構。寫React Native,通常navigation的部分都會用React Navigation。
PrimaryNav是一個stack,裡面define了可以push的container(page),以上圖為例,這個stack只能push LaunchScreen這個container。如果push了沒define的container進去,stack依然會是空的即使那個container已寫好。
initialRouteName寫的就是第一個會被push進stack的container。因此,上圖的app 第一個會顯示的page會是LaunchScreen.
如有更多對navigation的疑問,請在自行查找。
而page的文件就是放在container的資料夾裡。寫一個app最開始的部分莫過於UI,因此一開始最常開到的資料夾一定是Containers和Navigation。

每建立一個component或container,普遍來說都會有兩個檔案。
比如說,LaunchScreen就會有LaunchScreen.js 和LaunchScreenStyles.js 。
這個兩檔案。前者是container會用到的UI component,後者是container UI component的格式。
你以為ignite的功能只是幫你分好結構嗎?不只,當你需要開新的container, component, etc, 你都需要用到ignite,因為他會自動幫你將新加的東西放回boilerplate的結構。在project的directory輸入以下指令能看到教用教學。
ignite簡單來說,最常用的莫過於
ignite new YourProjectName
ignite g container YourContainerName
ignite g component YourComponentName假如我用了以上指令去generate一個新的container,contain.js和containerStyles.js也會自動建立並放到相應資料夾,同時於AppNavigation.js寫進stack。
現在,大家可以generate一個container ,開始試試自己的第一個頁面了!
當你generate一個新的container並運行於android,看到的畫面如果是這個就恭喜你,你成功了。

後記
心得去到這裡就差不多了。萬事起頭難,有手把手教學真的太好了﹗一旦開始了正式寫code的部分,就總有辦法寫下去,很多問題都可以找到要怎麼做。我很感歎如果可以省卻了這些初始的工作其實整件事會有趣很多。
要整理回這篇心得又花了幾天時間,因為我也忘記了某些步驟……有時記錄真的挺重要。
最後謝謝Playa大家的照顧和幫忙。
致和我一樣想開始又無從入手的新手。