Webpack + React router dynamic routing - how to catch require loading exception with require.ensure

801 views Asked by At

I'm using the react/react-router/webpack stack with dynamic routing between my different pages of the app which means every page loads asynchronously by demand. Everything works great but when I deploy a new version, my current active users who didn't fetch all of the Js file of all the pages will get stuck once the'll try to navigate to another page they haven't visited yet.

EXAMPLE

lets say I have a code split app with the following .js generated files and md5 (for cache busting):

main.123.js
profile.456.js

User visits my main page and gets only main.123.js.

In the meantime I deploy a new version with a different md5 (I made changes both in profile.js and main.js)

main.789.js
profile.891.js

User tries to navigate to the profile page, the app tries to fetch profile.456.js but get an error since the profile.js file has been swapped and there is now way for the app to know the new file name.

I came up with 2 solutions but none of them really solves the problem

SOLUTION 1? Always keep 2 versions available in production. but it's a slippery slope since 2 versions sometimes won't be enough. i.e User can leave his tab open for days so several deployments can take place until he decides to use the app again.

SOLUTION 2? catch the js loading exception and show the user a message to reload the app. This solution can work but it's an annoying UX if you ask me.

Has anyone experienced this kind of issue? Any suggestions?

EDIT - SOLUTION: I have decided to go with the second approach (solution 2). In order to catch the loading error I had to upgrade my webpack to webpack 2 since require.ensure implementation doesn't support catching require file exception. With webpack 2 I can use System.import which supports error handling. So basically what I did is:

dynamic loading my component using System.import :

function getMyComponent(nextState, cb, path) {
    return System.import('components/myComponent').then(module => {
        cb(null, module);
    });

}

And catching the error:

function getAsyncComponent(getComponent) {
    return function (nextState, cb, path) {
        getComponent(nextState, cb, path, store).catch(err => {
            store.dispatch(notificationSend({message: <span>Uh oh.. Something went wrong.}));
            cb(err);
        });
    }
}

<Route path="foo" getComponent={getAsyncComponent(getMyComponent)}/>
1

There are 1 answers

0
Paul S On

I would go for a variant of #2 where, upon detecting that a route file no longer exists on the server, you do a refresh for the user using window.location.reload(true). That should do a hard refresh which will load the new files.