React Native & Expo デバッグ方法いろいろ

私がシステム開発（環境整備）をする上でもっとも重要視しているのが**「デバッグ」**です。
プログラミングを始めたばかりのころはデバッグの重要性を知らず、
エラーやバグの原因が特定できずなんども挫折してしまった経験があります。

ただ、その重要性を知っていても、未経験の言語だったりフレームワークだったりすると設定が意外と難しかったり、ベストプラクティスが決まっておらず案外はまってしまったりするのではないでしょうか。

今回は`React Native` + `Expo` + `Typescript`でのアプリの開発を通して、様々なデバッグ方法を試してみたので設定方法も含めて残しておこうと思います。

今後`React Native`を使ってネイティブアプリ開発を始めようと考えている方の一番最初のハードルを取り払う足がかりになれば幸いです。

# <font color="LightSeaGreen">console.log<font>
javascriptではおなじみコマンドですが、react nativeでもコード内に仕込んでおけば、
アプリを起動しているコンソール上に結果を表示させることが可能です。
開発の初期段階で使う分には手軽に動きを確認できるのでおすすめです。
![68b54d05c873d09e61b947cc79c95b2b.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/498345/77e0ed2d-7840-64b4-04b3-220eeb2718cd.gif)
ただし、console.logを使ったデバッグは消し忘れの可能性があります。
その上、コードを汚すことになりアンチパターンとされているため実際の開発ではあまり好ましくはありません。

# <font color="LightSeaGreen">vscode / react native tools<font>
vscodeは本当に優れたEditerで、react nativeのデバッグもプラグイン（[React Native Tools](https://marketplace.visualstudio.com/items?itemName=msjsdiag.vscode-react-native)）を導入すれば、ブレイクポイントの配置とステップ実行が可能になります。

少し導入に苦戦したので、以下に導入方法を説明します。
※ 以下方法はExpoで開発しているのが前提条件となります。

### 1. vscodeで`React Native Tools`プラグインをインストール
![Extension__React_Native_Tools_—_merchandise_control_system_native_app.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/498345/af0d6fd6-3418-fa7c-58ae-da0a6891363c.png)

### 2. プロジェクトにvscodeのデバッグ設定(`launch.json`)を追加

ドキュメントには様々な開発パターンの設定が記載されていますが、Expoで開発するのであれば、以下の設定でIOSもAndroidも対応できます。


```json:.vscode/launch.json
{
  "version": "0.2.0",
  "configurations": [
    {
      "name": "Attach to packager",
      "cwd": "${workspaceFolder}",
      "type": "reactnative",
      "request": "attach",
      // Expoのデバッグポートにアタッチ
      "port": "19001"
    }
  ]
}

```

### 3. シュミレータ`IOS`もしくは`Android`を起動、デバッグモードをオンにする

シュミレータ上で以下、コマンドを打つことで開発者メニューを開くことができます。

IOSシュミレータ： `ctrl + cmd + z`
Androidシュミレータ： `cmd + m`

そのリストの中から`Debug Remote JS`をクリックします。

![508dab625c4ba8eddf989f421951b941.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/498345/68cf6fef-925b-7cf9-351a-24a7c570eebd.gif)

### 4. vscodeのコードの任意の箇所にブレイクポイントを配置、デバッグ実行

`Debug Remote JS`をオンにすると、ブラウザでdebugger-ui（http://localhost:19001/debugger-ui/) が開きますが、デバッグポートに対し一つのデバッガしかアタッチできないため、debugger-uiは閉じておきます。（ブラウザのタブを閉じればOKです）

その上でvscode上で先程`launch.json`で作成したデバッガを実行します。
コンソールの画面を`DEBUG CONSOLE`にすることで、デバッガがアタッチされたか確認することができます。
![a6cb0f9dc917c120e65040f5dfdd691d.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/498345/ce95f6a2-d649-f559-1458-67cd4638122d.gif)

### 5. シュミレータを操作し、ブレイクポイントの変数の値を確認
デバッガを実行している状態でシュミレータを操作すると、ブレイクポイントのコードに処理が走ったとき、そこで処理が一時停止します。
その状態で、以下の操作を行うことができます。

- 変数にカーソルをあてると、変数の内容を確認できる
- DEBUG CONSOLEで変数の内容を確認したり、さらに処理を一時的を加えることができる
- ステップ実行して変数の変化を確認
- 変数をwatchリストに加えて、意図通りに変数が変化するか確認

私が良く活用するのは上記ですが、他にも便利機能があるので、できることの詳細は公式サイトを見たほうがわかりやすいです。
> https://code.visualstudio.com/docs/editor/debugging#_debug-actions

![b177a9c2e3175e5cc04fbc478beffa6d.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/498345/9a43b9c6-6ba1-2f90-1b13-cdaf59faddb9.gif)



# <font color="LightSeaGreen">react native debugger</font>
`react native debugger`は別途アプリをインストールする必要がありますが、「UIのデバック」と「Reduxのデバッグ」を同じアプリ内で行うことができます。
`Hooks`のおかげで`Redux`の存在感は若干薄まりましたが、それでもなお導入しているプロダクトは多いのではないでしょうか。

### `react native debugger`ダウンロード

以下、リリースページから自分のOSにあうものをダウンロードできます。
> https://github.com/jhen0409/react-native-debugger/releases

※ MacであればHomebrewコマンドを打つだけでダウンロードできます。

```
brew update && brew cask install react-native-debugger
```

### `react native debugger`初期設定
ダウンロードしたアプリを開き、メニューバー → Debugger → Open Config Fileをクリックします。
`.rndebuggerrc`ファイルが開くので、デフォルトデバッグポートを以下に書き換えます。

```deff
// RNDebugger will open debugger window with the ports when app launched
-  defaultRNPackagerPorts: [8080],
+  defaultRNPackagerPorts: [19001],
```

これで、Expoのデバッガにアタッチできます。
上記vscodeにも記述しましたが、デバッグポートには一つのデバッガしか接続できないため`debugger-ui`もしくは`React Native Tools`は閉じておく必要があります。（複数のデバッガを並列実行する方法もあるかもしれませんが、私の調べた限りでは必要に応じて切り替えて使うのが一番簡単かと思います。）

### UIデバッグ
シュミレータ上で開発者メニューを開き「Show Element Inspecter」をオンにします。
あとはUIにカーソルを当てると、react native debugger上にUIのstyle・propsなどの状態を確認したり、書き換えたりすることも可能です。リアルタイムで変化を確認できるため大変便利です。

![e0cf3ced29f2aeb1370bc329c33a077f.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/498345/7bc95642-37b8-b34b-eb55-b2965ef9167f.gif)

### Reduxデバッグ

Reduxのデバッグを行うためには、まずプロダクトコードのstoreファイルに`__REDUX_DEVTOOLS_EXTENSION_COMPOSE__`の記述をおこないます。

公式サイトにはシンプルな記述方法が記載されています。
> https://github.com/zalmoxisus/redux-devtools-extension#11-basic-store

今回のプロダクトではこのような記述にしています。

```typescript:store.ts
import { createStore, applyMiddleware, combineReducers, compose } from 'redux';
import createSagaMiddleware from 'redux-saga';

import { reducer as reduxFormReducer } from 'redux-form';
import itemReducer from '../reducers/item';

import rootSaga from '../sagas/item';

/* eslint-disable no-underscore-dangle, @typescript-eslint/no-explicit-any */
const composeEnhancers =
  process.env.NODE_ENV === 'development' &&
  typeof window === 'object' &&
  (window as any).__REDUX_DEVTOOLS_EXTENSION_COMPOSE__
    ? (window as any).__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({})
    : compose;
/* eslint-enable */

const sagaMiddleWare = createSagaMiddleware();
const enhancer = composeEnhancers(applyMiddleware(sagaMiddleWare));

const store = createStore(
  combineReducers({
    item: itemReducer,
    form: reduxFormReducer,
  }),
  enhancer,
);

sagaMiddleWare.run(rootSaga);

export type AllState = ReturnType<typeof store.getState>;
export default store;
```

以下はredux-formの例ですが、Redux Actionの発行、Store Stateの変化まで一画面で確認することができます。
それ以外にもアクション名やpayloadの値も確認することができます。

![eb4c68e17f00ddce6f8a01ba04d347e7.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/498345/a910a916-27c9-d595-220e-daaddc8d5630.gif)





# <font color="LightSeaGreen">debugger-ui / chrome dev tools</font>

上記デバッグ手法を使えば、ほぼほぼ開発におけるデバッグは問題ないかと思いますが、`debugger-ui`と`Chrome dev tools`を使ったデバッグも可能です。
![ff88f4524f38b9a53f4a8d4487ae55db.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/498345/77585cdf-6000-fbce-9d41-bac5a7eb4ef7.png)


ただし、この手法は以下の理由から、今回の開発では使いませんでした。

- Expoの公式Debuggingドキュメント「Source maps and async functions」内でchrome dev tools内の`source map`と非同期関数は100%信用できないとの記述があること。（であれば、vscodeでステップ実行したほうが良い？）
    - https://docs.expo.io/workflow/debugging/#remote-debugging-with-chrome-developer-tools
- `Typescript`と`Webpack`の設定の影響か、うまく`source map`がうまく生成されなかった

Typescript・Webpackなどの設定を見直せばうまくデバッグできるようになるかもしれませんが、Expoを利用した開発では「vscode」と「react native debugger」で十分デバッグ機能は果たしていると考え、今回はあまり深く追求せず進めることにしました。
もし、このデバッグ手法にメリットを発見する、もしくはもっと簡単にデバッグができる方法を見つけたら記事を更新したいと思います。