Browser Tab State Synchronization with React hooks
In a web app, there's many times its useful to keep app state synchronized across browser tabs. The method described here uses the browser's localStorage API which is supported in most modern browsers. This allows tabs from web pages on the same domain to be able to talk to each other locally without communicating through a server.
This is just one way of doing this. Some other ways of doing this include cookie based synchronization, which is basically the same thing but we just read / write from cookies.
The gist of how this is done is the following snippets responsible for accessing the browser's local storage API:
window.addEventListener("storage", event => {
// This is fired whenever another tab writes something to localStorage
})
// Current tab writes to local storage
localStorage.setItem("key", "test")
Now applying this to a simple react component:
/ ComponentReadsFromLocalStorage.js
import React, { useState, useEffect } from "react"
// This is the key we use to communicate between tabs
const UNIQUE_STORAGE_KEY = "unique_key"
function ComponentReadsFromLocalStorage() {
const [counter, setCounter] = useState(0)
// Hook that receives updates from other tabs
useEffect(() => {
function onStorageUpdate(event) {
if (event.key !== UNIQUE_STORAGE_KEY || Number.isNaN(event.newValue)) {
return
}
setCounter(Number(event.newValue))
}
window.addEventListener("storage", onStorageUpdate)
return function cleanup() {
window.removeEventListener("storage", onStorageUpdate)
}
})
return (
<div>
<button
onClick={() => {
const newCounter = counter + 1
// Update current tab state
setCounter(newCounter)
// Update other tabs' state
localStorage.setItem(UNIQUE_STORAGE_KEY, newCounter)
}}
>
Counter
</button>
{counter}
</div>
)
}
export default ComponentReadsFromLocalStorage
Note that there isn't any error handling done here, there is a chance that localStorage.setItem
may fail due to lack of storage space, or perhaps the localStorage API may not exist for older browsers so you should be handling these for production apps