create-react-app: CRA 5.0 fails to hot-reload in a docker container built On shoulders of _____

Apolgozies for being super inexperience, there many layers and dont know which part of the problem I should debug like is it docker, is npm package manager, is it react-scripts@5.0.0 itself?

Describe the bug

CRA 5.0 fails to hot-reload in a docker container with using CHOKIDAR_USEPOLLING

Yes its mounted correctly. I checked many many times.

Environment

OS:Windows 10 VScode WSL Docker 4.2.0

Steps to reproduce

Lets say you

npx create-react-app my-app

you start the server it works normally, however your workflow at a company uses docker containers with react for development

you use the newer version of react-scripts@5.0.0

dockerfile created

WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
ENV PORT=3000
EXPOSE 3000
CMD ["npm", "run", "start"]

docker build -t react-test-2 ./

docker run -e CHOKIDAR_USEPOLLING=true -v D:\test-react-app\my-app:/app -it --name react-test-app2 -p 3000:3000 react-test-2

you triple check that bind mount is working. Try different directories, check docker, check Chokidar library,

So I went back to the version that worked with in react-scripts@4.0.3 it somehow works, try again different techniques

I see when I ran react-script@5.0.0

I get (node:31) [DEP_WEBPACK_DEV_SERVER_ON_AFTER_SETUP MIDDLEWARE] DeprecationWarning: ‘AfterStep Middleware’ option is deprecated. Please use the ‘setupMiddlewares’ option.

Or the webpack log displays on start up don’t know if its a issue or bug yet related https://github.com/facebook/create-react-app/issues/11871

Or maybe it’s

npm WARN read-shrinkwrap This version of npm is compatible with lockfileVersion@1, but package-lock.json was generated for lockfileVersion@2. I’ll try to do my best with it!

Expected behavior

React to hot-reload as it did in react-scripts@4.0.3 inside a mounted volume docker container

Actual behavior

React-scripts@5.0.0 fails to hot-reload inside a mounted volume docker container

Reproducible demo

https://github.com/Gresliebear/Reproducible-demo

Solution

Downgrade react-scripts@5.0.0 to react-scripts@4.0.3

However your will expose to vulerabilites which 2 are critical

image

About this issue

  • Original URL
  • State: open
  • Created 2 years ago
  • Reactions: 24
  • Comments: 56

Commits related to this issue

Most upvoted comments

Just sharing my workaround for CRA 5.0

// 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`);
    }
  }
}
// package.json
"scripts": {
    "start": "node ./setup && react-scripts start",
...

Running React container with either -e CHOKIDAR_USEPOLLING=true or -e WATCHPACK_POLLING=true solves the issue for me depending on the Webpack version I am using (I believe it was after version 5.72.1 that Webpack started using its own files watcher(Watchpack) as a replacement for Chokidar).

Could run it like so if you are unsure about the Webpack version: docker run -e CHOKIDAR_USEPOLLING=true -e WATCHPACK_POLLING=true <your_container_image>

Could also, of course, add those variables into the dockerfile so they are provided during the building of the image:

...
ENV CHOKIDAR_USEPOLLING=true
ENV WATCHPACK_POLLING=true
...

Any news in this issue?

Still no solution except of downgrading to 4.0.3? People stop breaking things pls.

I just wanted to share my “fix” for this for those of you who may be using docker compose and are possibly running multiple frontend containers. Originally I had to custom set two different WDS_SOCKET_PATH for two different containers, along with exposing their dev 3000 ports to different host ports in case I needed to directly access:

example1:  
  ports:
    - "3001:3000"
  environment:
    - WDS_SOCKET_PATH=/example1/sockjs-node
    - CHOKIDAR_USEPOLLING=true
    
example2:
  ports:
    - "3002:3000"
  environment:
    - WDS_SOCKET_PATH=/example2/sockjs-node
    - CHOKIDAR_USEPOLLING=true

This broke recently as everyone knows. My workaround ended up being:

example1:  
  ports:
    - "3001:3000"
  environment:
      - WDS_SOCKET_PORT=3001
      
example2:
  ports:
    - "3002:3000"
  environment:
      - WDS_SOCKET_PORT=3002

…which then falls back to using the default path, but a different port… allowing hot reload to work. CHOKIDAR_USEPOLLING was removed. note I have a traefik proxy in front of these via docker also.

After almost 24 hours trying to setup this dockerized react app with webpack, typescript and hot reload I finally did it.

I’m on a Windows 10 and this is my setup:

Folder structure

.
├── root-folder-with-docker-compose
│   ├── reactadmin-app
│   │   ├── package.json
│   │   ├── Dockerfile
│   ├── docker-compose.yml 
│   ├── .env

package.json

{
  "name": "reactadmin-app",
  "version": "0.1.0",
  "private": true,
  "dependencies": {
    "@testing-library/jest-dom": "^5.14.1",
    "@testing-library/react": "^13.0.0",
    "@testing-library/user-event": "^13.2.1",
    "@types/jest": "^27.0.1",
    "@types/node": "^16.7.13",
    "@types/react": "^18.0.0",
    "@types/react-dom": "^18.0.0",
    "ra-data-json-server": "^4.6.2",
    "react": "^18.2.0",
    "react-admin": "^4.6.2",
    "react-dom": "^18.2.0",
    "react-scripts": "5.0.1",
    "typescript": "^4.4.2",
    "web-vitals": "^2.1.0"
  },
  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test",
    "eject": "react-scripts eject"
  },
  "eslintConfig": {
    "extends": [
      "react-app",
      "react-app/jest"
    ]
  },
  "browserslist": {
    "production": [
      ">0.2%",
      "not dead",
      "not op_mini all"
    ],
    "development": [
      "last 1 chrome version",
      "last 1 firefox version",
      "last 1 safari version"
    ]
  }
}

Dockerfile

FROM node:18-alpine

WORKDIR /reactadmin-app
COPY package*.json .
RUN npm install
COPY . .
CMD npm start

docker-compose.yml

version: '3.8'
services:
  reactadmin-app:
    container_name: reactadmin-app
    build: ./reactadmin-app
    restart: unless-stopped
    env_file: ./.env
    ports:
      - $REACT_LOCAL_PORT:$REACT_DOCKER_PORT
    environment:
      - WATCHPACK_POLLING=true
      - WDS_SOCKET_HOST=127.0.0.1
      - WDS_SOCKET_PORT=$REACT_LOCAL_PORT
    stdin_open: true
    tty: true
    volumes:
      - $REACT_APP_PATH:/reactadmin-app
      - /reactadmin-app/node_modules

.env

REACT_APP_PATH="C:/dev/root-folder-with-docker-compose/reactadmin-app"
REACT_LOCAL_PORT=3000
REACT_DOCKER_PORT=3000

You can use VSCode Dev Containers extension to check if you are mounting/binding the volume/folders correctly (changes in a source file in your local machine should trigger changes in the same source file in the docker container) .

Make sure you have the following option checked in Docker Desktop docker

@rmarcelo that works great with my app, thanks! (Note, I removed the env check for my setup because it was redundant with our Docker configuration, so I can’t speak to that part working)

added WDS_SOCKET_HOST=127.0.0.1 and seems it solve the problem.

@svitan0k fix worked for me! Thanks! Running Docker Desktop on Windows

I don’t have that option, might be because I’m on Windows and you seem to be on macOS. This seems to be a WSL-specific issue I think. image

I tried to install version 4.0.3 separately, but webpack.config is the samethat is 5.0.0, all projects need to be updated to version 5, they force us to do so, and use this fix

It helped me

Just sharing my workaround for CRA 5.0

// 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`);
    }
  }
}
// package.json
"scripts": {
    "start": "node ./setup && react-scripts start",
...

and set WDS_SOCKET_PORT = current port on docker

@Makentosh you can remove the if (process.env.NODE_ENV === 'development') {} , I just put it there since I’m also using the setup.js for my npm build

@Makentosh hmm, that’s weird, I don’t see any reason why it should not work anymore on your end, the setup.js should trigger on npm start or yarn start which would replace performance:false, with the performance:false, + watchOptions config.

NODE_ENV work also in ./node_modules/react-scripts/config/webpack.config.js performance: false

@Makentosh How do you access it in the browser? do you use reverse proxy? could you try WDS_SOCKET_PORT=0 ?