Docker＋create-react-appで環境構築すると編集内容が更新されない

# 発生した問題
Docker上でcreate-react-appを使用して環境構築を行ったところ、ソースコードを編集してもページが更新されない問題が発生しました。
また、ブラウザを更新しても編集内容はページに反映されません。

現時点（2022/4/29）では、日本語の記事で「Docker＋Reactの環境構築でホットリロードが効かない場合、docker-compose.ymlと.envファイルで「CHOKIDAR_USEPOLLING=true」を設定すると解決する。」等、似たような問題が見つかりましたが、いずれも自分の環境では解決しませんでした。

# 使用した開発環境
開発に使用した環境は下記になります。

```
OS:　　　            Windows10 Pro
エディタ:　　　　　　 VSCode
コンソール:　　　　　 powershell
パッケージマネージャ：Yarn
```

その他、依存関係は下記になります。

```
Docker:          20.10.14
Docker Compose:  2.4.1
yarn:            1.22.18
create-react-app:5.0.1
react:           18.1.0
react-dom:       18.1.0
react-scripts:   5.0.1
typescript:      4.6.4
```

# 問題の発生手順
ホスト側で下記ディレクトリ構成のファイルを用意します。

```
project/
　├ docker-compose.yml
　└ Dockerfile
```

```docker-compose.yml
version: "3.8"

services:
  web:
    build: .
    volumes:
      - .:/usr/src/app
    command: sh -c "cd front && yarn start"
    ports:
      - 3000:3000
```

```Dockerfile:Dockerfile
FROM node:16.15.0
WORKDIR /usr/src/app
```

ホスト側の`docker-compose.yml`のあるディレクトリに移動し、下記コマンドでプロジェクトを生成します。

```powershell:powershell
docker-compose run --rm web `
 sh -c "yarn create react-app front --template typescript"
```

プロジェクトが生成されたら、先ほどと同じ階層でwebコンテナを立ち上げる下記コマンドを実行します。

```powershell:powershell
docker-compose up -d
```

webコンテナ上のサーバーの準備ができたら`http://localhost:3000`にアクセスできることを確認します。

次に、ホスト側の`App.tsx`の内容を書き換えて保存してブラウザを更新しますが、編集内容は反映されません。

# 解決策
下記サイトを参考に、webpackのwatchOptionsの設定を上書きすることで解決したのでその手順を紹介します。
[CRA 5.0 fails to hot-reload in a docker container built On shoulders of _____ #11879](https://github.com/facebook/create-react-app/issues/11879)

## setup.jsでwatchOptionsを上書きする手順
まずは、ホスト側のpackage.jsonにある`scripts`の`start`の定義を下記のように書き換えます。

```package.json
"scripts": {
    "start": "node ./setup && react-scripts start",
...
```

（[CRA 5.0 fails to hot-reload in a docker container built On shoulders of _____ #11879](https://github.com/facebook/create-react-app/issues/11879)より引用）

次に、ホスト側の`front`フォルダ直下に`setup.js`を作成し下記を記載します。

```setup.js
const fs = require('fs');
const path = require('path');

if (process.env.NODE_ENV === 'development') {
  const webPackConfigFile = path.resolve('./node_modules/react-scripts/config/webpack.config.js');
  let webPackConfigFileText = fs.readFileSync(webPackConfigFile, 'utf8');

  if (!webPackConfigFileText.includes('watchOptions')) {
    if (webPackConfigFileText.includes('performance: false,')) {
      webPackConfigFileText = webPackConfigFileText.replace(
        'performance: false,',
        "performance: false,\n\t\twatchOptions: { aggregateTimeout: 200, poll: 1000, ignored: '**/node_modules', },"
      );
      fs.writeFileSync(webPackConfigFile, webPackConfigFileText, 'utf8');
    } else {
      throw new Error(`Failed to inject watchOptions`);
    }
  }
}
```

（[CRA 5.0 fails to hot-reload in a docker container built On shoulders of _____ #11879](https://github.com/facebook/create-react-app/issues/11879)より引用）

ホスト側の`docker-compose.yml`のある階層で、webコンテナを立ち上げる下記コマンドを実行します。

```powershell:powershell
docker-compose up -d
```

webコンテナ上のサーバーの準備ができたら`http://localhost:3000`にアクセスできることを確認します。

この時点で解決した報告がありますが、自分の場合はホスト側の`App.tsx`の内容を書き換えてブラウザを更新しても編集内容は反映されませんでした。

原因は、setup.jsの実行時点では`process.env.NODE_ENV`が`undefined`になり、`if (process.env.NODE_ENV === 'development') {...}`の条件式から抜けているためでした。
※下記のようにログを出力して`setup.js`の条件式に入る直前の`process.env.NODE_ENV`の状態を確認しました。

```setup.js
const fs = require('fs');
const path = require('path');

console.log('process.env.NODE_ENV', process.env.NODE_ENV); //追加
...
```

なので、`setup.js`を下記の通りコメントアウトしてみます。

```setup.js
const fs = require('fs');
const path = require('path');

// if (process.env.NODE_ENV === 'development') {
  const webPackConfigFile = path.resolve('./node_modules/react-scripts/config/webpack.config.js');
  let webPackConfigFileText = fs.readFileSync(webPackConfigFile, 'utf8');

  if (!webPackConfigFileText.includes('watchOptions')) {
    if (webPackConfigFileText.includes('performance: false,')) {
      webPackConfigFileText = webPackConfigFileText.replace(
        'performance: false,',
        "performance: false,\n\t\twatchOptions: { aggregateTimeout: 200, poll: 1000, ignored: '**/node_modules', },"
      );
      fs.writeFileSync(webPackConfigFile, webPackConfigFileText, 'utf8');
    } else {
      throw new Error(`Failed to inject watchOptions`);
    }
  }
// }
```

次に、ホスト側の`docker-compose.yml`のある階層で、webコンテナを立ち上げる下記コマンドを実行します。

```powershell:powershell
docker-compose restart
```

webコンテナ上のサーバーの準備ができたら`http://localhost:3000`にアクセスできることを確認します。

画面が表示されたら、`App.tsx`の内容を書き換えて保存すると、数秒遅れて画面が更新されることが確認できました。

一応、最終的なディレクトリ構成を載せておきます。

```
project/
　├ front/
　│　├ node_modules/
　│　├ public/
　│　├ src/
　│　├ .gitignore
　│　├ package.json
　│　├ README.md
　│　├ setup.js
　│　├ tsconfig.json
　│　├ yarn.lock
　├ docker-compose.yml
　└ Dockerfile
```

# 参考
[CRA 5.0 fails to hot-reload in a docker container built On shoulders of _____ #11879](https://github.com/facebook/create-react-app/issues/11879)