Use React Hooks to Share States Between your Service Workers
Communicating across tabs and windows
Building a progressive web app (PWA) usually stands for instant access regardless of network state, but a PWA can offer much more through the use of service workers, including state share across multiple browser tabs and windows.
Sharing the state across tabs can have multiple utilities — for this specific example, let’s think about an e-commerce cart that updates every time we add or remove products. We can efficiently achieve a state share across tabs and windows by combining service workers with React Hooks. Let’s explore that.
We’ll start declaring the cart counter in our state and create the functions responsible to increment and decrement the counter of products on the bag. You can call these functions on a call-to-action button inside your product component, for instance.
Before working with service workers, we must make sure the feature is available in the browser — only then can we safely register our
serviceWorker.js. By convention, the service worker's availability is verified with
'serviceWorker' in navigator, but coding otherwise does not affect the results anyhow.
Considering we don’t need any actions during the transition to load the page, we’ll apply the best practice to register the service worker after the page is loaded, thus avoiding any performance gaps.
Our goal here is as soon as the service worker is ready to add a listener to the message event. In such a manner, when the event is triggered and there’s data available, we can update the state of our cart with the value sent through the service worker.
Note that we need to inject the
setCounter function from our state and the
sw variable we’ve declared to use the
useEffect Hook and update the counter every time a product is added or removed from the bag. In another note, I'm using the optional chaining operator, to supposedly improve readability and leave the code cleaner.
Setting Up the Service Worker
serviceWorker.js, we’ll bind a function to the message event, from where we’ll retrieve the data coming along with the event and the ID of the client (browser tab or window). I'm using the destructuring assignment syntax to unpack only the values I need.
message event is triggered, we’ll call the
matchAll method of the
Clients interface. This returns a
Promise for a list of service-worker
Client objects, which may represent a browser tab or window. This will help us get all, and only, the service-worker clients controlled by the service worker we have registered in our app.
Once we have our tabs and windows list (
clients), we go through each one and verify if that’s not the current client. In this case, we send a message with the data we received in our event using the
postMessage method. This method allows our service worker to send a message to a client. This message, in turn, is received in the
message event on
At this point, we’re able to connect the action (postMessage) from the service worker file with the listener we have declared in our React app. The next step is to trigger the action from our React app towards our service worker.
Just before we go to our next step, I’d recommend adding
skipWaiting to the install event of our service worker. This method forces the waiting service worker to become the active service worker. It also ensures any updates to the underlying service worker will take effect immediately for both the current client and all other active clients, offering a better user experience for our use case.
Wrapping Up the Solution
When the user adds or removes a product to the bag from one window, they’ll see the state updating immediately on the screen. Now we need a function to tell our service worker to update all other tabs and windows.
The controller is a read-only property of the
ServiceWorkerContainer interface and returns a
ServiceWorker object, which inherits methods from its parent,
Worker, where the
postMessage method is available.
Finally, we call our
stateToServiceWorker function inside our very own auxiliary function to decrement and increment the state. The cycle is complete — we can send updates to the service worker and receive from it, keeping multiples tabs and windows with a concise state.
Below you can find the complete code of this use case.
Live Project: https://csb-nxdkl-pjh3c3wz3.now.sh/
Thank you for reading!