Skip to content

Commit

Permalink
feat: Manual periodic SW update (#8)
Browse files Browse the repository at this point in the history
BREAKING CHANGE: useServiceWorker hook now takes an object with two optional parameters
  • Loading branch information
mircostraessle authored Feb 24, 2021
1 parent 1d9d718 commit 904cb7f
Show file tree
Hide file tree
Showing 3 changed files with 46 additions and 21 deletions.
20 changes: 7 additions & 13 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 1 addition & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,15 +33,14 @@
"homepage": "https://github.com/smartive/react-pwa-toolbox#readme",
"dependencies": {
"tslib": "^1.10.0",
"workbox-window": "^4.3.1"
"workbox-window": "^6.1.1"
},
"peerDependencies": {
"react": "^16.8.0 || ^17.0.0"
},
"devDependencies": {
"@types/node": "^12.12.20",
"@types/react": "^16.9.16",
"@types/workbox-window": "^4.3.3",
"@typescript-eslint/eslint-plugin": "^2.6.0",
"@typescript-eslint/parser": "^2.6.0",
"eslint": "^6.6.0",
Expand Down
44 changes: 38 additions & 6 deletions src/hooks/use-service-worker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,29 +4,61 @@ import { Workbox } from 'workbox-window';
let workbox: Workbox | null = null;
let updateCallback: () => void = () => window.location.reload();

export const useServiceWorker = (scriptUrl = 'service-worker.js'): [boolean, () => void | null] => {
type Props = {
scriptUrl?: string;
periodicUpdateInterval?: number;
};

export const useServiceWorker = (props?: Props): [boolean, () => void | null] => {
const [isServiceWorkerUpdateAvailable, setServiceWorkerUpdateAvailable] = useState(false);

useEffect(() => {
if ('serviceWorker' in navigator && process.env.NODE_ENV !== 'development') {
workbox = new Workbox(scriptUrl);
let periodicUpdateHandler: NodeJS.Timeout | undefined;

workbox.addEventListener('waiting', () => setServiceWorkerUpdateAvailable(true));
// Inspired by https://developers.google.com/web/tools/workbox/guides/advanced-recipes#offer_a_page_reload_for_users
if ('serviceWorker' in navigator && process.env.NODE_ENV !== 'development') {
workbox = new Workbox(props?.scriptUrl || 'service-worker.js');
let registration: ServiceWorkerRegistration | undefined;

updateCallback = () => {
if (!workbox) {
return;
}

// Assuming the user accepted the update, set up a listener
// that will reload the page as soon as the previously waiting
// service worker has taken control.
workbox.addEventListener('controlling', () => {
window.location.reload();
});

workbox.messageSW({ type: 'SKIP_WAITING' });
if (registration && registration.waiting) {
// Send a message to the waiting service worker,
// instructing it to activate.
workbox.messageSkipWaiting();
}
};

workbox.register().then((registration) => !!registration.waiting && setServiceWorkerUpdateAvailable(true));
// Add an event listener to detect when the registered
// service worker has installed but is waiting to activate.
workbox.addEventListener('waiting', () => setServiceWorkerUpdateAvailable(true));

workbox.register().then((r) => {
registration = r;

// If a periodic update interval is set we manually check for service worker updates in this intervall
// (otherwise only the browser would check for updates e.g. on page navigations)
if (registration && props?.periodicUpdateInterval) {
periodicUpdateHandler = setInterval(() => registration?.update(), props.periodicUpdateInterval);
}
});
}

return () => {
if (periodicUpdateHandler) {
clearInterval(periodicUpdateHandler);
}
};
}, []);

return [isServiceWorkerUpdateAvailable, updateCallback];
Expand Down

0 comments on commit 904cb7f

Please # to comment.