In many React applications, it's common to track changes to state or localStorage values across components, and for this, developers often rely on state management tools like Redux
or the Context API
. These tools allow you to keep your state in sync within the React component tree. However, a significant challenge arises when you need to update the state from a non-React
component (for example, external scripts or browser events). Ensuring that this update is reflected in other React components can be difficult, as it requires careful management of state updates and dispatching actions across different parts of the application.
Tracking localStorage Changes Across Tabs and Active Sessions in React
Managing localStorage in React can be challenging, especially when you need to track changes across different browser tabs or within the same active tab. Imagine having a user logged into your app across multiple tabs—if they log out or update a setting in one tab, wouldn’t it be great to have these changes reflect instantly across all tabs?
-
In this guide, we’ll explore two useEffect examples to handle these cases:
- Tracking localStorage events in different tabs.
- Tracking events both across tabs and within the same active tab.
Example 1: Basic Multi-Tab localStorage Event Tracking
Let’s start with the first useEffect
, which tracks localStorage
changes across multiple tabs. Imagine you have a user logged into several tabs. If they log out in one, you want to log them out everywhere to maintain security.
useEffect(() => {
const handleStorageChange = (event) => {
if (event.type === 'storage') {
const token = localStorage.getItem('token');
if (!token) {
cleanLocalStorage();
setLogin("");
navigate("/login");
}
}
};
window.addEventListener('storage', handleStorageChange);
return () => {
window.removeEventListener('storage', handleStorageChange);
};
}, []);
How This Works:
- We’re adding an event listener for the
storage
event, which detects changes inlocalStorage
from other browser tabs. - When a user logs out in one tab (removing their token from
localStorage
), the hook catches this change, and the user is logged out in all other open tabs.
Feature:
- This hook allows for cross-tab sync, giving a seamless logout experience if the user leaves a tab open and inactive.
Drawback:
- This only listens to
storage
events, which means changes made within the same tab (like clearinglocalStorage
) won’t trigger any actions.
Example 2: Advanced Multi-Tab and Same-Tab Syncing
Now, let’s look at an upgraded approach for handling localStorage
changes across tabs and within the same active tab. This approach is helpful if your app needs to respond to changes made in the current tab as well.
useEffect(() => {
const handleStorageChange = (event) => {
if (event.type === "storage" || event.type === "localStorageChanged") {
if (skipPath.includes(window.location.pathname)) {
return;
}
const token = localStorage.getItem("token");
if (!token) {
dispatch({ type: "LOGOUT" });
clearData();
navigate("/login", { replace: true });
}
}
};
window.addEventListener("storage", handleStorageChange);
window.addEventListener("localStorageChanged", handleStorageChange);
return () => {
window.removeEventListener("storage", handleStorageChange);
window.removeEventListener("localStorageChanged", handleStorageChange);
};
}, []);
To trigger the localStorageChanged event in a non-React component, use the following code when updating localStorage within the same tab. This ensures that changes are properly detected and can be handled accordingly in your application.
window.dispatchEvent(new Event("localStorageChanged"));
How This Works:
- We add event listeners for both the
storage
event and a custom event calledlocalStorageChanged
. - With the custom event, we can trigger
handleStorageChange
within the same active tab, allowing single-tab changes to also be detected. -
skipPath
provides a way to exclude specific routes from the logout functionality, letting you customise where automatic logout should and shouldn’t happen.
Features:
-
Same-Tab Sync: The custom event,
localStorageChanged
, lets you detect local changes within the same tab. This can be especially useful for managing user preferences or login sessions. -
Selective Path Exclusion: By using
skipPath
, you can exclude specific routes, such as settings or payment pages, from triggering a logout. - Cross-Tab Sync: Like the first hook, this one still syncs changes across tabs, ensuring a cohesive user experience.
Drawbacks:
- Added Complexity: With custom events and path exclusions, managing the code can get a bit more complex, especially if you have many routes or conditional cases.
-
Potential Overhead: Listening to multiple events could slightly impact performance, especially if there are frequent changes to
localStorage
.
Real-World Example: Secure, Seamless Logout
Think of a social media app open in several tabs. A user logs out in one tab, and all tabs should log them out immediately to prevent security risks. Similarly, if they’re on a secure settings page, you may want to avoid sudden logouts by excluding this route.
With these hooks, you can:
- Automatically log the user out of all tabs when they log out in one.
- Avoid logging them out unexpectedly from certain pages.
- Detect changes in both multi-tab and single-tab contexts for a smoother experience.
Final Thoughts
Tracking localStorage
events across tabs and within the same active session can significantly enhance user experience, especially for multi-tab security and state consistency.
Both useEffect
hooks work together without interfering with each other, allowing for both cross-tab and single-tab functionality. Whether it’s a logout action or a user setting change, this approach ensures users see an up-to-date, consistent experience across your entire app.
By understanding and combining these approaches, you can make your app feel more responsive and secure for users, adapting dynamically to user actions across tabs and sessions.
Top comments (0)