React Native project initial (更新:2019.03.13)
===
## 摘要
>這份文件蒐集 react native 專案 initial 前置步驟。follow 一樣的步驟、和其他前輩建立同樣的環境,能加速日後(頻繁的)debug。
## 目錄
[TOC]
---
一、環境:
---
### 1. iterm2 + oh-my-zsh
https://pjchender.blogspot.tw/2017/02/mac-terminal-iterm-2-oh-my-zsh.html
### 2. nvm install (更新:2019.03.13)
照 nvm 官網
注意:要先安裝 zsh 再安裝 nvm, npm,否則 $PATH 仍會指向 bash,還要去調環境變數才抓得到 npm (更新:2019.03.13)
### 3. 安裝模擬器
照 react-native 官網
### 4. 安裝 react-native-debugger
https://github.com/jhen0409/react-native-debugger
simulator 要 command + D open "Remote JS debugging"
---
二、Initial
---
相關指令可參考 package.json 或輸入 yarn run 查看全部
### 1. 安裝node module
```
$ yarn install
```
### 2. initial 模擬器
**iOS環境:**
```
$ yarn start // 開啟另一個terminal,並前往當前project路徑下執行此命令。
$ yarn install:ios --simulator="iPhone 7s"
```
**Android 環境:**
```
$ yarn start // 開啟另一個terminal,並前往當前project路徑下執行此命令。
$ yarn install:android
```
**yarn install:android 前須注意:**
1. 模擬機需開啟 - api 選 28+ (更新:2018.11.27)
2. google service 需安裝 - Android Studio preference/appearance & behavior/system settings/android sdk/Google play service version 49 (更新:2018.11.27) 因firebase 所需
3. Java 版本 - [8 (更新:2018.11.27)](https://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html "jdk8")
4. sdk、ndk 照 [官網教學 0.55 (更新:2018.11.27)](https://facebook.github.io/react-native/docs/0.55/building-from-source) 安裝,ndk 安裝 [r10b 版本 (更新:2019.03.13)](https://dl.google.com/android/repository/android-ndk-r10b-darwin-x86_64.zip)
**萬一需重新安裝 (例如:android 有相關安裝錯誤),follow 以下指令:**
先中斷 server 後,
```
$ cd android (現在不用了)
$ ./gradlew clean // for clean android related setting (現在不用了)
$ cd .. (現在不用了)
$ yarn clean // clean nodemodule
$ yarn // install nodemodule
$ yarn install:android // 一樣在下指令前,須檢查注意事項
```
**每次啓 android 模擬器後,需輸入以下設定,才能登入:**
```
$ adb reverse tcp:9999 tcp:9999
$ adb reverse tcp:9998 tcp:9998
```
### 3. yarn generator 新增檔案

### 4. yarn test api

### 5. 專案會自動更新 node version
每次進入目錄後便會自動安裝,當升級 node 版本時,需移除一檔案:
```
$ rm -rf .prerequisite
```
選 default node version 時,需下指令:
```
$ node list
```
三、認識套件
---
這個專案使用一些外部套件,陳列如下:
### react-native-google-analytics-bridge
GA related.
* official website: https://github.com/idehub/react-native-google-analytics-bridge
### React Navigation
其中的切換 tab 功能 tabBarOnPress
* official website(v1): https://v1.reactnavigation.org/docs/tab-navigator.html
* demo - tabBarOnPress:
```
const MyTabNavigator = TabNavigator({
Track: {
screen: MyStackNavigator,
},
Details: {
screen: FundDetailsScreen,
}
}, {
tabBarPosition: 'bottom',
tabBarOptions: {
upperCaseLabel: false,
activeTintColor: '#7562DB',
inactiveTintColor: '#7562DB',
pressColor: '#7562DB',
indicatorStyle: { backgroundColor: '#6458A8' },
style: {
backgroundColor: '#fff'
}
},
navigationOptions: ({ navigation }) => ({
tabBarOnPress: (scene, jumpToIndex) => {
console.log('onPress:', scene.route);
jumpToIndex(scene.index);
},
}),
});
```
### DvaJS
* official website: https://dvajs.com
* demo - model:
```
app.model({
// namespace: model 的命名空间,同时也是他在全局 state 上的属性,只能用字符串,不支持通过 . 的方式创建多层命名空间。
namespace: 'todo',
// state: 初始值,优先级低于传给 dva() 的 opts.initialState。
state: [],
// reducers: 以 key/value 格式定义 reducer。用于处理同步操作,唯一可以修改 state 的地方。由 action 触发。
reducers: {
add(state, { payload: todo }) {
// 保存数据到 state
return [...state, todo];
},
},
// effects: 以 key/value 格式定义 effect。用于处理异步操作和业务逻辑,不直接修改 state。由 action 触发,可以触发 action,可以和服务器交互,可以获取全局 state 的数据等等。
effects: {
*save({ payload: todo }, { put, call }) {
// 调用 saveTodoToServer,成功后触发 `add` action 保存到 state
yield call(saveTodoToServer, todo);
yield put({ type: 'add', payload: todo });
},
},
// subscriptions: 以 key/value 格式定义 subscription。subscription 是订阅,用于订阅一个数据源,然后根据需要 dispatch 相应的 action。在 app.start() 时被执行,数据源可以是当前的时间、服务器的 websocket 连接、keyboard 输入、geolocation 变化、history 路由变化等等。格式为 ({ dispatch, history }, done) => unlistenFunction。注意:如果要使用 app.unmodel(),subscription 必须返回 unlisten 方法,用于取消数据订阅。
subscriptions: {
setup({ history, dispatch }) {
// 监听 history 变化,当进入 `/` 时触发 `load` action
return history.listen(({ pathname }) => {
if (pathname === '/') {
dispatch({ type: 'load' });
}
});
},
},
});
// reference official website: https://dvajs.com/api/#model
```
### withFormik HoC
* brief intro: Wrap our **form** with the using withFormik HoC
* official website: https://github.com/jaredpalmer/formik#the-gist
* demo
```
import { withFormik } from 'formik';
const form = withFormik({
// Transform outer props into form values
mapPropsToValues: props => ({
email: props.email,
}),
validationSchema: props => {
Keyboard.dismiss();
if (props.showCaptcha) {
return object().shape({
email: string()
.email('Email 格式怪怪的? 請確認一下 : )')
.required('請填入註冊時驗證的 Email : )'),
password: string().required('請填入密碼'),
captcha: string().required('請填入驗證碼'),
});
} else {
return object().shape({
email: string()
.email('Email 格式怪怪的? 請確認一下 : )')
.required('請填入註冊時驗證的 Email : )'),
password: string().required('請填入密碼'),
});
}
},
// Submission handler
handleSubmit: (
values,
{
props,
setSubmitting
}
) => {
// setSubmitting(false);
Keyboard.dismiss();
props.doSignIn(values);
},
validateOnChange: false,
displayName: 'form(Login)',
});
// !!! Wrap our form with the using withFormik HoC
export default form(Component);
//reference component: src/landing/components/Login/form.js
```
### Immer
* official website: https://www.npmjs.com/package/immer
* brief intro: you will apply all your changes to a temporarily draftState, which is a proxy of the currentState. Once all your mutations are completed, Immer will produce the nextState based on the mutations to the draft state.

* basic concept
`produce(currentState, producer: (draftState) => void): nextState`
* demo
```
import produce from "immer"
const baseState = [
{
todo: "Learn typescript",
done: true
},
{
todo: "Try immer",
done: false
}
]
const nextState = produce(baseState, draftState => {
draftState.push({todo: "Tweet about it"})
draftState[1].done = true
})
// reference: official website
```
```
import produce from 'immer';
export default (state, { payload }) =>
produce(state, draftState => {
draftState.activate = true;
draftState.close = false;
});
// reference component: src/landing/states/cac/reducers/internals/activate.js
```
### react-native-mimetype
> https://www.npmjs.com/package/react-native-mimetype`
```
import mime from 'react-native-mimetype';
const UploadFile = async ({ onUploadFile, response, setFieldValue, downloads }) => {
const uri = new URI(response.uri);
const filename = response.fileName || uri.filename();
const mimeType = mime.lookup(filename);
const data = await onUploadFile({ filename, contentType: mimeType, fileUri: response.uri });
if (data.code === 200) {
const file = data.data;
const targetDownloads = assign({}, downloads, file);
console.log(targetDownloads);
setFieldValue('downloads', file);
tracker.trackEvent('Activity', 'ActFileSuccess', { label: mimeType });
}
};
```
* MIME type:
> https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Basics_of_HTTP/MIME_types
>
四、其他操作
---
### 更換字體方式
> https://medium.com/@kswanie21/custom-fonts-in-react-native-tutorial-for-ios-android-76ceeaa0eb78