Patrick Desjardins Blog
Patrick Desjardins picture from a conference

Detecting a New Version of your React-Redux App in 15 minutes

Posted on: 2018-09-12

While I am well aware of the feature of notifying to the user that a new version is ready to be served, I never implemented a solution myself. Gmail and many websites do it to allow the user to refresh the website which brings the new JavaScript, CSS and other assets. It is a great way to encourage users to refresh the page once in a while. With Web application, it occurs that some users do not refresh so often. Myself, I rarely refresh my Gmail webpage until I see the notification.

This week, someone raised the point that it would be interesting to have this feature on the application I am working on at Netflix. I wish I had time to do it, but instead of procrastinating, I decided to give it a try. The idea was to implement a way as cheap as possible since I have a backlog that is gargantuan. 

If you recall, a few days ago, I discussed how to burn the Git hash into your React application. The article described how to inject in the environment variable the Git hash of the actual build. It is done at compilation time and the React has a way to read the information from JavaScript. In my case, the version is stored in an environment variable named REACT.APP_VERSION. It is accessible at runtime. That being said, the version remains on the client browser until the user fetches the new JavaScript file that has a new value which happens only on a new build. The idea is to leverage the fact that if the client keeps the version until a new version is available, on a refresh, then checking once in a while for a version change is a matter of comparing the Git hash on the client machine to the actual head of the repository.

The idea can be broke down in few steps:

  1. On build, burn the Git Hash into the JavaScript
  2. On build, generate a file with the Git Hash
  3. Fetch once in a while from the server a file that has the latest Git
  4. If the Git Hash from the JavaScript environment variable is different from the file on the server, show a message to the user to refresh

The first step has been already discussed, but in short, it consists to change the npm run build to take the head Git hash and to pass it down to React.

 "build": "REACT_APP_VERSION=$(git rev-parse --short HEAD) react-scripts-ts build"

The second step require a change on the build system. At Netflix, we are using Jenkins. I added a Bash Script step that look like the following:

CommitHash=$(git rev-parse --short HEAD) 
echo "HASH" 
echo $CommitHash 
echo $CommitHash > "public/version.json" 
echo "VERSION FILE BEGIN" cat public/version.json 
echo "VERSION FILE END" 

The step number is open to how you want to handle it. I personally want to avoid setTimout. I rather have a Redux Middleware that will check once in a while. The middleware holds the time of the last check and on every action, verify that the time has elapsed from the threshold that you set. I have mine to 5 minutes. Every 5 minutes, or more depending of when the user does an action, it fetches the static file. As you can see in the previous step, the Git Hash is stored in a file under the public folder which is accessible with a simple Ajax call. If the string is different from the JavaScript environment variable, an action is dispatched that shows a message to the user to refresh the page.

 const diff = this.currentTimeSinceEpoch() - this.lastCheck; 
 if (diff > SECONDS_BETWEEN_VERSION_CHECK) { 
   this.lastCheck = this.currentTimeSinceEpoch(); 
   (async () => { 
     const currentVersion = process.env.REACT_APP_VERSION === undefined ? "dev" : process.env.REACT_APP_VERSION; 
     try { 
        const file = await AjaxLibraryThatYouUse<string>({ 
         request: { url: "/version.json" 
         } 
        }); 
        const newVersion = file.result; 
        if (newVersion !== currentVersion) { 
          next(SharedActions.actionInfoMessage("New version available!")); 
        } 
      } catch (error) { 
        // Track error here 
      } 
    })(); 
  } 

The solution took less than writing this article! It might not be a push notification, neither having a complete Rest API. It is not the most elegant because we fetch every 5 minutes, but at the end of the day, a new feature is born in 15 minutes of work. Now, I'm back to my backlog!