React Native on Expo の環境構築 for Docker

:::note info
こちらは 2023年10月 時点での情報です
:::
# Expo
React native用開発プラットフォーム。
基本的な登録方法はページ末尾に記載

# react-native 環境構築
結構回りくどいかも。
## 初期構成（例）
react-nativeというプロジェクトディレクトリの配下に
- `myapp` ディレクトリ（任意のディレクトリ名）
    - 配下に `Dockerfile` を設置
- `docker-compose.yml` を置いておく
```bash
├── react-native
│   ├── myapp
│   │   └── Dockerfile
│   └── docker-compose.yml
```

## Dockerfile
```Dockerfile
FROM node:18-slim

WORKDIR /myapp

COPY . .

RUN apt update && apt install -y bash

RUN yarn global add expo

RUN yarn install

CMD yarn start
```

nodeのバージョンは expo 公式リファレンスの以下の文章から　`node:18-slim` を選択

![スクリーンショット 2023-10-22 18.37.02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/374872/505d4f1c-e4a0-d27d-2f0c-d58940efe53a.png)

参考

https://docs.expo.dev/get-started/installation/#requirements

## docker-compose.yml
```dockerfile
version: '3'
services:
  myapp:
    container_name: myapp-react-native
    tty: true
    build:
      context: ./myapp
      dockerfile: ./Dockerfile
    volumes:
      # - node_modules:/myapp/node_modules
      - ./myapp:/myapp
    # env_file: ./myapp/.env
    ports:
      - 8081:8081
# volumes:
#   node_modules:
```

#### 注1
`node_modules` は別途名前ボリュームとして設定している。
ディレクトリをコンテナとマウントしているため、イメージ内で `yarn install` 時にホスト側の `node_modules` も同期されてしまうのを避けるためです。

#### 注2
一旦 `node_modules` に関する設定をコメントアウトしているのは、`expo` のプロジェクトを作成する際に、作成先のディレクトリにファイルがあるとエラーになるから。合わせてDockerfileも一旦 `app` ディレクトリから避難させておく。

### イメージビルド
```
docker compose build
```

## expo関連プロジェクト作成
### コンテナに入る
```
docker compose run --rm app bash --login
```

### expoアプリ作成
```
yarn create expo-app -t
```
以下が出るので、選択
`myapp` 内に作成したいので、`app name` は `.` にする
```
success Installed "create-expo-app@2.1.1" with binaries:
      - create-expo-app
✔ Choose a template: › Navigation (TypeScript)
✔ What is your app named? … .
✔ Downloaded and extracted project files.
> yarn install
yarn install v1.22.19
```

インスコ完了後に `Dockerfile` を元に戻す。
そしたら以下のような構成になるはず

```
$ tree -L 2
.
├── docker-compose.yml
└── myapp
    ├── Dockerfile
    ├── app
    ├── app.json
    ├── assets
    ├── babel.config.js
    ├── components
    ├── constants
    ├── metro.config.js
    ├── node_modules
    ├── package.json
    ├── tsconfig.json
    └── yarn.lock
```

そして `docker-compose.yml` の `node_modules` に関するコメントアウトを外す。

```Dockerfile
version: '3'
services:
  myapp:
    container_name: myapp-react-native
    tty: true
    build:
      context: ./myapp
      dockerfile: ./Dockerfile
    volumes:
      - node_modules:/myapp/node_modules
      - ./myapp:/myapp
    # env_file: ./myapp/.env
    ports:
      - 8081:8081
volumes:
  node_modules:
```

### アプリ起動
```
docker compose up
```

すると以下のようなQRコードと一緒に表示されるはず

```
Attaching to myapp-react-native
yarn run v1.22.19
$ expo start
myapp-react-native  | Starting project at /myapp
myapp-react-native  | Starting Metro Bundler
myapp-react-native  | ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
myapp-react-native  | █ ▄▄▄▄▄ █ ██▀▀ ▀▄██ ▄▄▄▄▄ █
myapp-react-native  | █ █   █ █  ▀█ ▀█▄▄█ █   █ █
myapp-react-native  | █ █▄▄▄█ █▀  █▄▀▀▄██ █▄▄▄█ █
myapp-react-native  | █▄▄▄▄▄▄▄█▄█ ▀▄█▄█ █▄▄▄▄▄▄▄█
myapp-react-native  | █▄▄██ ▄▄▀▀█▄█▄▀▄ ▄██ ▀▄▄ ▄█
myapp-react-native  | █▀ ▄█▀ ▄▄▄ ▄█▀█▀ ▀▀  ▄  ▀██
myapp-react-native  | █▀█▀▀▀█▄▄ ▀▀▄▀▄ ▀▄▀▄▀▀▄ ▀██
myapp-react-native  | ███▀▄█ ▄ ▀▀  ▄ ▄▄▄ ██▄ ▀███
myapp-react-native  | █▄▄█▄▄▄▄█ █ █▄ ▀▄ ▄▄▄ ▀ ▄▄█
myapp-react-native  | █ ▄▄▄▄▄ █▀▀ █▀█▀▀ █▄█ ▀▀▀██
myapp-react-native  | █ █   █ █▄▀▀▄▀▄▄█▄▄ ▄▄▀   █
myapp-react-native  | █ █▄▄▄█ █▀█▀ ▄ ▄▄▄▄ ▀█▀▀ ██
myapp-react-native  | █▄▄▄▄▄▄▄█▄█▄█▄▄█████▄▄▄▄▄▄█
myapp-react-native  | 
myapp-react-native  | › Metro waiting on exp://127.0.0.1:8081
myapp-react-native  | › Scan the QR code above with Expo Go (Android) or the Camera app (iOS)
myapp-react-native  | 
myapp-react-native  | › Web is waiting on http://localhost:8081
myapp-react-native  | 
myapp-react-native  | › Using Expo Go
myapp-react-native  | › Press s │ switch to development build
myapp-react-native  | 
myapp-react-native  | › Press a │ open Android
myapp-react-native  | › Press w │ open web
myapp-react-native  | 
myapp-react-native  | › Press j │ open debugger
myapp-react-native  | › Press r │ reload app
myapp-react-native  | › Press m │ toggle menu
myapp-react-native  | › Press o │ open project code in your editor
myapp-react-native  | 
myapp-react-native  | › Press ? │ show all commands
```

注意
SDK 48以下のExpoは `port: 19000` で起動していたが、SDK 49以上は `port: 8081` で起動するようになった

### Webアクセス
webからアクセス（http://localhost:8081）すると、以下のように表示される
![スクリーンショット 2023-10-22 19.19.04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/374872/c3abf709-e384-bd96-b940-1783425a2d30.png)

### expo goからアクセス
expo goだと同じWi-Fiで同じIPアドレスを指定しないと接続できない。
以下でipアドレスを探す。
```
ifconfig | grep "192"
        inet xx.xx.xx.xx netmask 0xffffff00 broadcast xx.xx.xx.255
```
inetの内容を、 `myapp/.env` に作成する。
```text
REACT_NATIVE_PACKAGER_HOSTNAME=xxx.xxx.xx.xxx
```

そして `docker-compose.yml` の　`env_file` の項目のコメントアウトを外して再度 `docker compose up` する
（もし `.expo` がないとか言われたらもう一回イメージを作り直してみるのをお勧めします）

すると expo で待機されているIPアドレスが変わる。

```
myapp-react-native  | › Metro waiting on exp://192.168.10.102:8081
myapp-react-native  | › Scan the QR code above with Expo Go (Android) or the Camera app (iOS)
```

そして表示されているQRコードをカメラで読み込むと、以下のようにBuild完了後、アプリが表示される。

![無題のプレゼンテーション (28).jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/374872/332fdc56-a6fe-0f64-04a8-547b1afdf641.jpeg)

# トラブルシューティング
## Expo Go のほうで同じLAN内のIPを参照しているのになんかアクセスできない...
一部の方には、以下のようなエラーが出るかもしれません。

```
There was problem loading requested app.it looks like you may be using LAN URL. Make sure your devise is on the same network as the server or try using a tunnel.
```

「起動しているアプリと、Expo goで同一LAN内のIPで接続できていないよ！」という意味で、「ちゃんと設定しているのに。。。」となっている方は、以下の手法を使ってください。

### `--tunnel` オプションで `Expo` を起動する
`package.json` の `start` コマンドを以下のように変更。

```
"start": "expo start --tunnel",
```

コンテナ内に入って、以下を実行する。

参考

https://docs.expo.dev/more/expo-cli/#tunneling

### @expo/ngrok のインスコ
```
yarn global add @expo/ngrok 
```

ビルドし直して起動してみる。

## npm パッケージ関連でなんか反映されないんだけど
`node_modules` をコンテナとイメージで切り離しているので、うまく動かない可能性もある
その時は、コンテナからホストに　`node_modules` を持ってきてみる。多分うまく行く。

```
docker cp myapp:/myapp/node_modules ./myapp/
```


# おまけ
## Expo アカウント登録
### sign up
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/374872/791d80fe-360d-d7d1-ff72-e4d260b7bde8.png)


### 基本情報登録
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/374872/ca0aedd4-4198-8121-a227-417eda5fc4f5.png)


### 画面
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/374872/7fd2e9d1-28ca-e839-d598-5aa2f4c4906c.png)


## Expo go
### インスコ
- Google Play
https://play.google.com/store/apps/details?id=host.exp.exponent&pli=1

- App Store
https://apps.apple.com/jp/app/expo-client/id982107779

### 上で作ったアカウントでログイン
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/374872/60f0904a-0c22-22c0-3c0f-99b8c951d9a7.png)

# 参考
Expo: Create your first app

https://docs.expo.dev/tutorial/create-your-first-app/