I'm using React 17, Workbox 5, and react-scripts 4. I created a react app with PWA template using:
npx create-react-app my-app --template cra-template-pwa
I use BackgroundSyncPlugin from workbox-background-sync for my offline requests, so when the app is online again, request will be sent automatically. The problem is I don't know when the request is sent in my React code, so I can update some states, and display a message to the user.
How can I communicate from the service worker to my React code that the request is sent and React should update the state?
Thanks in advance.
You can accomplish this by using a custom onSync
callback when you configure BackgroundSyncPlugin
. This code is then executed instead of Workbox's built-in replayRequests()
logic whenever the criteria to retry the requests are met.
You can include whatever logic you'd like in this callback; this.shiftRequest()
and this.unshiftRequest(entry)
can be used to remove queued requests in order to retry them, and then re-add them if the retry fails. Here's an adaption of the default replayRequests()
that will use postMessage()
to communicate to all controlled window
clients when a retry succeeds.
async function postSuccessMessage(response) {
const clients = await self.clients.matchAll();
for (const client of clients) {
// Customize this message format as you see fit.
client.postMessage({
type: 'REPLAY_SUCCESS',
url: response.url,
});
}
}
async function customReplay() {
let entry;
while ((entry = await this.shiftRequest())) {
try {
const response = await fetch(entry.request.clone());
// Optional: check response.ok and throw if it's false if you
// want to treat HTTP 4xx and 5xx responses as retriable errors.
postSuccessMessage(response);
} catch (error) {
await this.unshiftRequest(entry);
// Throwing an error tells the Background Sync API
// that a retry is needed.
throw new Error('Replaying failed.');
}
}
}
const bgSync = new BackgroundSyncPlugin('api-queue', {
onSync: customReplay,
});
// Now add bgSync to a Strategy that's associated with
// a route you want to retry:
registerRoute(
({url}) => url.pathname === '/api_endpoint',
new NetworkOnly({plugins: [bgSync]}),
'POST'
);
Within your client page, you can use navigator.seviceWorker.addEventListener('message', ...)
to listen for incoming messages from the service worker and take appropriate action.