React Native Expo の開発環境を Dev Containers で構築
React Native に入門しました。Expo を使った React Native の開発環境を Dev Containers で構築してみたので、今回はそちらの構築手順を紹介してみます。
Dev Containers とは
Docker コンテナを開発環境として利用する VS Code の拡張機能です。必要なライブラリやツール類、VS Code の設定や拡張をコンテナに詰め込むことにより、コンテナ内で開発を完結させることが可能です。また、こうした環境の定義を JSON ファイルに記述するのですが、その JSON ファイルを共有するだけで、メンバ間で同じ環境を構築することができます。詳しくはこちら。
個人的に大変気に入っており、最近は個人開発でもチーム開発でもこればっかり使っています。
事前準備
- VS Code をインストールしておいてください。
- Docker Desktop をインストールしておいてください。
- VSCode に Dev Containers 拡張 をインストールしておいてください。
- お手元のスマートフォンに Expo Go をインストールしておいてください。
最終的なコード
構築手順に入る前に、先に最終的なコードを貼っておきます。
コンテナを立ち上げてみる
まずは最小限の構成でコンテナを立ち上げてみましょう。
作業用のディレクトリを作成し、そのディレクトリを VS Code で開いておいてください。
作業用ディレクトリのルートに .devcontainer
という名前のディレクトリを作成し、その中に devcontainer.json
という名前の JSON ファイルを作成します。
devcontainer.json
に以下を記述し、コンテナのベースとなるイメージを指定します。
{
"name": "Expo Dev Container",
"image": "mcr.microsoft.com/devcontainers/typescript-node:20"
}
記述できたら、コマンドパレットを開き、Dev Containers: Reopen in Container
を選択してコンテナを立ち上げてみましょう。
↓ 立ち上がった様子
Expo を導入する
create-expo-app
で新規のプロジェクトをセットアップしましょう。ターミナルから以下のコマンドを実行してください。プロジェクト名は自由に設定していただいて OK です。
npx create-expo-app my-expo-project --template
--template
オプションを付けておくとプロジェクトのテンプレートを選択できます。
今回は Expo Router を使用したかったため、Navigation (TypeScript) - File-based routing with TypeScript enabled
を選択しました。
プロジェクトの作成が完了すると、my-expo-project
ディレクトリの配下にファイル・ディレクトリ群が生成されます。
ここでは階層は必要無いため、my-expo-project
ディレクトリ の中身をルートディレクトリに移動させてしまいます。併せて、app
, assets
, components
, constants
ディレクトリは src
ディレクトリにまとめておきます (Navigation (TypeScript)
以外のテンプレートを選択した場合は、テンプレートに合わせて良い感じにしてください。)。
mv ./my-expo-project/* ./my-expo-project/.* .
rm -r ./my-expo-project/
mkdir src
mv app/ assets/ components/ constants/ src/
続いて、Expo Go からコンテナ内で立ち上げた expo サーバにアクセスできるように設定していきます。
Expo Go と expo サーバが立ち上がっているマシンは、同一のネットワークに接続している必要があります。しかし、今のままでは、expo サーバはコンテナの IP アドレスで立ち上がってしまうため、Expo Go からアクセスできない状態です。
そこで、expo サーバがホストマシンのローカル IP アドレスで立ち上がるように設定するとともに、expo サーバが使用する 8081 ポートをホストの 8081 ポートにマッピングすることで、この問題を解決します。
まず、.devcontainer
配下に .env
ファイルを作成し、REACT_NATIVE_PACKAGER_HOSTNAME
にホストマシンのローカル IP アドレスを指定します。
REACT_NATIVE_PACKAGER_HOSTNAME=192.168.xxx.xxx
続いて、devcontainer.json
を編集します。
{
"name": "Expo Dev Container",
"image": "mcr.microsoft.com/devcontainers/typescript-node:20",
"forwardPorts": [
8081
],
"runArgs": [
"-p=8081:8081",
"--env-file",
".devcontainer/.env"
]
}
devcontainer.json
の編集が完了したら、コンテナを再起動して設定を反映させましょう。コマンドパレットを開き、Dev Containers: Rebuild Container
をクリックします。
コンテナの再起動が完了したら、expo サーバを立ち上げてみましょう。ターミナルから以下のコマンドを実行し、表示された QR コードをスマートフォンの Expo Go でスキャンしてください。
npm run start
無事サンプルアプリケーションが表示されれば成功です。
なお、プロジェクトのディレクトリ構成を変更したためにパス解決のエラーが出るかもしれません。その場合は、ターミナルに表示されたメッセージに従ってパスを修正してください。
linter と formatter を導入する
この後もファイルの編集が続くので、linter と formatter を導入しておきましょう。
ESLint + Prettier が定番かと思いますが、今回は Biome を使ってみます。簡単設定 & 爆速でとても良い感じ。
ターミナルから以下のコマンドを実行し、Biome のインストール & 設定ファイルの作成を実施します。
npm install --save-dev @biomejs/biome
npx @biomejs/biome init
biome.json
が生成されれば OK です。今回は、デフォルトの設定に、インデントの設定と横幅の設定を追加し、以下のようにしてみました。
{
"$schema": "https://biomejs.dev/schemas/1.4.1/schema.json",
"formatter": {
"enabled": true,
"indentStyle": "space",
"indentWidth": 2,
"lineWidth": 120
},
"linter": {
"enabled": true,
"rules": {
"recommended": true
}
},
"organizeImports": {
"enabled": true
}
}
続いて、VSCode の拡張機能を導入しましょう。
devcontainer.json の customizations > vscode > extensions
にて、コンテナにインストールする拡張機能を指定することができます。
devcontainer.json
を編集し、biomejs.biome
を追加します。ついでに、私がよく使う拡張機能を加えています。好みに応じて修正していただければと思います。
{
"name": "Expo Dev Container",
"image": "mcr.microsoft.com/devcontainers/typescript-node:20",
"forwardPorts": [8081],
"runArgs": ["-p=8081:8081", "--env-file", ".devcontainer/.env"],
"customizations": {
"vscode": {
"extensions": [
"biomejs.biome",
"eamodio.gitlens",
"editorconfig.editorconfig",
"streetsidesoftware.code-spell-checker",
"VisualStudioExptTeam.vscodeintellicode"
]
}
}
}
加えて、Biome の設定を追記しておきます。
devcontainer.json
の customizations > vscode > settings
にて、.vscode/settings.json
に記述するような設定を追加することができます。
{
"name": "Expo Dev Container",
"image": "mcr.microsoft.com/devcontainers/typescript-node:20",
"forwardPorts": [8081],
"runArgs": ["-p=8081:8081", "--env-file", ".devcontainer/.env"],
"customizations": {
"vscode": {
"extensions": [
"biomejs.biome",
"eamodio.gitlens",
"editorconfig.editorconfig",
"streetsidesoftware.code-spell-checker",
"VisualStudioExptTeam.vscodeintellicode"
],
"settings": {
"editor.formatOnSave": true,
"editor.codeActionsOnSave": {
"quickfix.biome": true,
"source.organizeImports.biome": true
},
"[javascript]": {
"editor.defaultFormatter": "biomejs.biome"
},
"[javascriptreact]": {
"editor.defaultFormatter": "biomejs.biome"
},
"[typescript]": {
"editor.defaultFormatter": "biomejs.biome"
},
"[typescriptreact]": {
"editor.defaultFormatter": "biomejs.biome"
},
"[json]": {
"editor.defaultFormatter": "biomejs.biome"
},
"[jsonc]": {
"editor.defaultFormatter": "biomejs.biome"
}
}
}
}
}
devcontainer.json
の編集が完了したら、Rebuild Container
をクリックし、コンテナを再起動しましょう。再起動が完了すると、Biome の VSCode 拡張が有効になっており、ファイルを保存すると自動で整形が働くようになっているはずです。devcontainer.json
か biome.json
で試してみてください。
この時点で一旦、既存のファイルに linter と formatter を適用しておきましょう。ターミナルから以下のコマンドを実行してください。
npx @biomejs/biome check --apply-unsafe ./*
私が試した時は dangerouslySetInnerHTML
を使用している箇所のみ自動での修正ができずエラーが出てしまいましたが、ここでは一旦スルーします。
ついでに、EditorConfig 拡張用の設定ファイル .editorconfig
をルートディレクトリに作成しておきます。
[*]
indent_style = space
indent_size = 2
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
パッケージのインストールが自動で走るようにする
コンテナ起動時に npm install
が実行されるようにしておきましょう。
まず、devcontainer.json
を編集し、postCreateCommand
を追記します。ここで指定したスクリプトが、コンテナ起動後に実行されることとなります。
{
"name": "Expo Dev Container",
"image": "mcr.microsoft.com/devcontainers/typescript-node:20",
"postCreateCommand": "./.devcontainer/postCreateCommand.sh",
"forwardPorts": [8081],
"runArgs": ["-p=8081:8081", "--env-file", ".devcontainer/.env"],
"customizations": {
"vscode": {
"extensions": [
"biomejs.biome",
"eamodio.gitlens",
"editorconfig.editorconfig",
"streetsidesoftware.code-spell-checker",
"VisualStudioExptTeam.vscodeintellicode"
],
"settings": {
"editor.formatOnSave": true,
"editor.codeActionsOnSave": {
"quickfix.biome": true,
"source.organizeImports.biome": true
},
"[javascript]": {
"editor.defaultFormatter": "biomejs.biome"
},
"[javascriptreact]": {
"editor.defaultFormatter": "biomejs.biome"
},
"[typescript]": {
"editor.defaultFormatter": "biomejs.biome"
},
"[typescriptreact]": {
"editor.defaultFormatter": "biomejs.biome"
},
"[json]": {
"editor.defaultFormatter": "biomejs.biome"
},
"[jsonc]": {
"editor.defaultFormatter": "biomejs.biome"
}
}
}
}
}
続いて、postCreateCommand.sh
を作成しましょう。
#/bin/bash
npm install
作成できたら、ターミナルから以下のコマンドを実行し、postCreateCommand.sh
に実行権限を付与してください。
chmod +x .devcontainer/postCreateCommand.sh
完了したら、Rebuild Container
をクリックし、コンテナを再起動しておきます。
ディスクのパフォーマンスを改善する
Dev Containers では通常、ファイルをローカルに保存し、それらをコンテナに対してバインドするような形式をとっています。こうした方式で node_modules をバインドしていると、
- パッケージのインストール
- Linting, Formatting
- 開発用サーバの起動
といった動作が非常に緩慢となり、大変ストレスです。そこで、名前付きボリュームを利用することでこの問題を解決します (詳細を知りたい方はこちらをご参照ください)。
devcontainer.json
にマウントの設定を追記します。
{
"name": "Expo Dev Container",
"image": "mcr.microsoft.com/devcontainers/typescript-node:20",
"postCreateCommand": "./.devcontainer/postCreateCommand.sh",
"mounts": ["source=node_modules_${devcontainerId},target=${containerWorkspaceFolder}/node_modules,type=volume"],
"forwardPorts": [8081],
"runArgs": ["-p=8081:8081", "--env-file", ".devcontainer/.env"],
"customizations": {
"vscode": {
"extensions": [
"biomejs.biome",
"eamodio.gitlens",
"editorconfig.editorconfig",
"streetsidesoftware.code-spell-checker",
"VisualStudioExptTeam.vscodeintellicode"
],
"settings": {
"editor.formatOnSave": true,
"editor.codeActionsOnSave": {
"quickfix.biome": true,
"source.organizeImports.biome": true
},
"[javascript]": {
"editor.defaultFormatter": "biomejs.biome"
},
"[javascriptreact]": {
"editor.defaultFormatter": "biomejs.biome"
},
"[typescript]": {
"editor.defaultFormatter": "biomejs.biome"
},
"[typescriptreact]": {
"editor.defaultFormatter": "biomejs.biome"
},
"[json]": {
"editor.defaultFormatter": "biomejs.biome"
},
"[jsonc]": {
"editor.defaultFormatter": "biomejs.biome"
}
}
}
}
}
併せて、postCreateCommand.sh
を以下のように編集します。
#/bin/bash
sudo chown node node_modules
npm install
完了したら、Rebuild Container
をクリックし、コンテナを再起動しておきます。
パスエイリアスを導入する
最後に、src
ディレクトリへのエイリアスとして @
を設定します。
まず、tsconfig.json
の compilerOptions
に
"baseUrl": ".",
"paths": {
"@/*": ["./src/*"]
}
を追記します。
{
"extends": "expo/tsconfig.base",
"compilerOptions": {
"strict": true,
"baseUrl": ".",
"paths": {
"@/*": ["./src/*"]
}
},
"include": [
"**/*.ts",
"**/*.tsx",
".expo/types/**/*.ts",
"expo-env.d.ts"
]
}
併せて、babel.config.js
の plugins
に
[
"module-resolver",
{
root: ["."],
alias: { "@": "./src" },
extensions: [".ts", ".tsx", ".js", ".ios.js", ".android.js"],
},
],
を追記します。
module.exports = (api) => {
api.cache(true);
return {
presets: ["babel-preset-expo"],
plugins: [
// Required for expo-router
"expo-router/babel",
[
"module-resolver",
{
root: ["."],
alias: { "@": "./src" },
extensions: [".ts", ".tsx", ".js", ".ios.js", ".android.js"],
},
],
],
};
};
これで、src
ディレクトリを基準にパスを指定できるようになりました。デフォルトでは全て相対パスになっているため、必要に応じて修正しておきましょう。
まとめ
本記事では、Dev Containers で React Native Expo の開発環境を構築する手順を紹介しました。筆者は React Native 初心者ですので、誤りや改善点があればご指摘いただけると大変助かります。
最後にもう一度、本記事で構築した環境のリポジトリを貼っておきます。
最後までお付き合いいただきありがとうございました。良き Dev Containers ライフを👋
Discussion