I prefer to let the user refresh rather than refreshing automatically (this prevents the potential for an infinite refresh loop bug).
The following strategy works well for a React app, code split on routes:
Strategy
Set your index.html to never cache. This ensures that the primary file that requests your initial assets is always fresh (and generally it isn’t large so not caching it shouldn’t be an issue). See MDN Cache Control.
Use consistent chunk hashing for your chunks. This ensures that only the chunks that change will have a different hash. (See webpack.config.js snippet below)
Don’t invalidate the cache of your CDN on deploy so the old version won’t lose it’s chunks when a new version is deployed.
Check the app version when navigating between routes in order to notify the user if they are running on an old version and request that they refresh.
Finally, just in case a ChunkLoadError does occur: add an Error Boundary. (See Error Boundary below)
Snippet from webpack.config.js (Webpack v4)
From Uday Hiwarale:
optimization: {
moduleIds: 'hashed',
splitChunks: {
cacheGroups: {
default: false,
vendors: false,
// vendor chunk
vendor: {
name: 'vendor',
// async + async chunks
chunks: 'all',
// import file path containing node_modules
test: /node_modules/,
priority: 20
},
}
}
Error Boundary
import React, { Component } from 'react'
export default class ErrorBoundary extends Component {
constructor(props) {
super(props);
this.state = { hasError: false };
}
static getDerivedStateFromError(error) {
// Update state so the next render will show the fallback UI.
return { hasError: true, error };
}
componentDidCatch(error, errorInfo) {
// You can also log the error to an error reporting service
console.error('Error Boundary Caught:', error, errorInfo);
}
render() {
const {error, hasError} = this.state
if (hasError) {
// You can render any custom fallback UI
return <div>
<div>
{error.name === 'ChunkLoadError' ?
<div>
This application has been updated, please refresh your browser to see the latest content.
</div>
:
<div>
An error has occurred, please refresh and try again.
</div>}
</div>
</div>
}
return this.props.children;
}
}
Note: Make sure to clear the error on an internal navigation event (for example if you’re using react-router) or else the error boundary will persist past internal navigation and will only go away on a real navigation or page refresh.