Search code examples
reactjsfirebasefirebase-hosting

Refresh build deployed on firebase hosting on changing route or detecting new build present


Question: Is it possible for us to check for the new build deployed on firebase hosting on changing route(or on focusing the tab) and reloading the page so user can view new features?

Currently user have to refresh the site to see new feature deployed in the build.

The following are the dependencies installed currently in my project:

"dependencies": {
    "@material-ui/core": "^4.5.0",
    "antd": "^3.23.6",
    "axios": "^0.19.0",
    "env-cmd": "^10.0.1",
    "firebase": "^7.4.0",
    "highcharts": "^7.2.0",
    "highcharts-react-official": "^2.2.2",
    "history": "^4.10.1",
    "loaders.css": "^0.1.2",
    "local-storage": "^2.0.0",
    "moment": "^2.24.0",
    "node-sass": "^4.12.0",
    "notistack": "^0.9.2",
    "qs": "^6.9.0",
    "query-string": "^6.9.0",
    "react": "^16.12.0",
    "react-dom": "^16.12.0",
    "react-ga": "^2.7.0",
    "react-google-login": "^5.0.7",
    "react-icons": "^3.7.0",
    "react-loaders": "^3.0.1",
    "react-mentions": "^3.3.1",
    "react-redux": "^7.1.1",
    "react-router-dom": "^5.1.2",
    "react-scripts": "3.1.2",
    "redux": "^4.0.4",
    "redux-thunk": "^2.3.0"
  }

Thats how my firebase.json config looks like currently:

{
  "hosting": {
    "target": "build",
    "public": "build",
    "ignore": ["firebase.json", "**/.*", "**/node_modules/**"],
    "rewrites": [
      {
        "source": "**",
        "destination": "/index.html"
      }
    ],
    "headers": [
      {
        "source": "**/*.@(jpg|jpeg|gif|png|css|js|jsx|scss|ttc|woff)",
        "headers": [{ "key": "Cache-Control", "value": "no-cache" }] \\ so user doesn't have to force refresh build
      }
    ]
  }
}

Possible Solutions:

  • Store the build number and add listener for when it is updated and reload the page when it does.
  • Check for build number/timestamp and match it with firebase hostings latest build somehow.

Can someone please guide me in the correct direction? I have created an issue on Github as well. Please let me know if there is some further detail required from me.


Solution

  • I was able to figure out a solution for my problem. I ended up adding a version Collection in my Cloud Firestore Database which gets updated from my CI/CD pipeline through a script which parse the current version of the package.json file and store it in the Cloud Firestore. And in my main App component of React i have added a Listener to version collection which lets compares version and reload page accordingly.

    Added REACT_APP_VERSION=$(node -pe \"require('./package.json').version\") before script (start/build) to set version from package.json

    In App.jsx added below listener in componentDidMount

    addVersionListener = () => {
    var env = process.env.REACT_APP_ENV === "staging" ? "staging" : "production";
    
    db.collection("AppVersion")
      .doc(env)
      .onSnapshot(docSnapshot => {
        if (docSnapshot.exists) {
          let data = docSnapshot.data();
    
          let shouldReload = compareVersions(
            data.version,
            process.env.REACT_APP_VERSION
          ); // compare-versions npm module
    
          if (shouldReload === 1) {
            window.location.reload();
          }
        }
      });
    };
    

    Script that runs after deployment REACT_APP_VERSION=$(node -pe \"require('./package.json').version\") node ./src/updateVersionNumber.js. In updateVersionNumber.js

    const firebase = require("firebase/app");
    const { credentials } = require("./firebase-credentials.js");
    
    if (firebase.apps.length === 0) {
      firebase.initializeApp(credentials);
    }
    
    require("firebase/firestore");
    
    const updateVersion = () => {
      var db = firebase.firestore();
    
      var env = process.env.REACT_APP_ENV === "staging" ? "staging" : "production";
    
      db.collection("AppVersion")
        .doc(env)
        .get()
        .then(async response => {
          if (response.exists && process.env.REACT_APP_VERSION) {
            try {
              await response.ref.update({ version: process.env.REACT_APP_VERSION});
            } catch (err) {
              console.log(err);
            }
          }
        });
    };
    
    updateVersion();
    

    To further manage the auto refresh for cases like when user is filling some form or doing some tasks which will make him loose his/her progress if page reloads automatically, I have used React Redux state to detect and halt reloading for time being and do it later by updating the above listener method in App.jsx.