Using chrome.tabs.onActivated
for auto update state

Rajat Dhoot
28 May 2024 - 05 Mins read
Using chrome.tabs.onActivated
to Update Data in Content Script from Options Script
In many Chrome extensions, you might need to update the content script with data changed in the options script. For example, imagine you are building an extension that allows users to configure settings in the options page, and these settings need to be reflected immediately in the content script of the active tab. The chrome.tabs.onActivated
listener can help you achieve this.
The Use Case
Suppose you have an options script where users can change the configuration of your extension. Once the settings are updated, you want the content script to fetch the new settings whenever the user switches to a different tab. This ensures that the content script always has the latest data without requiring a manual refresh.
Step-by-Step Implementation
1. Background Script
First, the background script will listen for tab activation events and send a message to the active tab's content script to fetch the updated data.
// Adds an event listener that triggers when a tab is activated (focused).
chrome.tabs.onActivated.addListener(function (info) {
// Queries the active tab within the current window.
chrome.tabs.query({ active: true }, function (tabs) {
// Sends a message to the active tab to perform an action (e.g., updating the tab).
chrome.tabs.sendMessage(tabs[0].id, { action: "SETTINGS_UPDATED" });
});
});
2. Options Script
In the options script, you save the updated settings in Chrome's storage. When the settings are changed, a message can also be sent to the background script to trigger an update.
Here's an example of how you can implement the options script using React:
import useStore from "@root/src/hooks/useStore";
import "@pages/options/Options.css";
import { setStorageData } from "@root/src/utils";
const Options = () => {
const [state, dispatch] = useStore();
const saveSettings = async () => {
await setStorageData(state);
chrome.runtime.sendMessage({ action: "SETTINGS_UPDATED", payload: state });
};
return (
<div className="min-h-screen justify-between items-center flex">
<div className="flex flex-col m-auto w-1/2 space-y-4">
<h1 className="text-5xl">Options</h1>
<input
type="text"
className="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500"
value={state.name}
onChange={(e) =>
dispatch({
type: "UPDATE_SETTINGS",
payload: { name: e.target.value },
})
}
/>
<button
className="text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:outline-none focus:ring-blue-300 font-medium rounded-lg text-sm w-full sm:w-auto px-5 py-2.5 text-center dark:bg-blue-600 dark:hover:bg-blue-700 dark:focus:ring-blue-800"
onClick={saveSettings}
>
Save
</button>
<p className="text-sm">
Changes done here get reflected to content script without
automatically
</p>
</div>
</div>
);
};
export default Options;
3. Content Script
In the content script, use React hooks to manage state and listen for messages from the background script. When the SETTINGS_UPDATED
message is received, fetch the updated settings.
import logo from "@assets/img/logo.png";
import useStore from "@root/src/hooks/useStore";
export default function App() {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const [state, dispatch] = useStore();
return (
<div className="text-black border-2 p-2">
<div className="bg-indigo-200 h-full w-full flex justify-center items-center">
<div className="text-center">
<div className="flex justify-center">
<img
src={chrome.runtime.getURL(logo)}
alt="Launchify"
className="h-12 w-12"
/>
</div>
<h1 className="text-3xl">Welcome to {state.name}</h1>
<h1 className="text-xl">Let's get started</h1>
</div>
</div>
</div>
);
}
4. App State
import { useEffect, useReducer } from "react";
import { getStorageData } from "@root/src/utils";
// Reducer function to handle state changes
function reducer(state, action) {
switch (action.type) {
// Action to set the initial state
case "UPDATE_SETTINGS":
return {
...state,
...action.payload,
};
// Default case returns the current state
default:
return state;
}
}
// Custom hook to manage state using useReducer and useEffect
const useStore = () => {
// Initialize the state and dispatch function using useReducer
const [state, dispatch] = useReducer(reducer, {
name: "Launchify",
});
// useEffect hook to handle side effects
useEffect(() => {
// Add a message listener to listen for messages from the background script or other parts of the extension
chrome.runtime.onMessage.addListener(async (message) => {
switch (message.action) {
// Handle the 'SETTINGS_UPDATED' action sent from the background script
case "SETTINGS_UPDATED": {
// Currently does nothing, but can be extended with further logic to update the component state or perform other actions
const response = await getStorageData(Object.keys(state));
dispatch({ type: "UPDATE_SETTINGS", payload: response });
return true;
}
}
return true; // Indicates asynchronous response
});
// Cleanup function to remove the message listener when the component is unmounted or dependencies change
return () => {
chrome.runtime.onMessage.removeListener(() => {});
};
}, [dispatch, state]); // Dependencies array for useEffect, re-runs the effect when dispatch or state changes
// Return the current state and dispatch function from the hook
return [state, dispatch];
};
export default useStore;
/*
* This code defines a custom hook, `useStore`, which manages state using `useReducer` and listens for messages from the Chrome extension runtime.
*
* 1. The `reducer` function handles state changes based on action types.
* - The 'SET_INIT_STATE' action sets the initial state with the name 'Launchify'.
* - The default case returns the current state if the action type is not recognized.
*
* 2. The `useStore` hook initializes state using `useReducer` with the `reducer` function and initial state.
* - `state` represents the current state.
* - `dispatch` is a function to dispatch actions to update the state.
*
* 3. The `useEffect` hook is used to add and remove a message listener.
* - `chrome.runtime.onMessage.addListener` listens for messages sent from other parts of the extension.
* - The listener checks the action type of the received message.
* - If the action type is 'SETTINGS_UPDATED', it currently does nothing but can be extended with further logic.
* - This is where you would handle the 'SETTINGS_UPDATED' action sent from the background script to update the component state or perform other actions as needed.
* - For example, you might update the state with new data related to the active tab.
* - The cleanup function returned by `useEffect` removes the message listener to prevent memory leaks.
* - The dependencies array `[dispatch, state]` ensures the effect runs when these dependencies change.
*
* 4. The `useStore` hook returns the current state and the dispatch function for use in React components.
*
* 5. `export default useStore` makes the custom hook available for import in other parts of the application.
*/
Explanation
-
Background Script:
chrome.tabs.onActivated.addListener
: Adds an event listener that triggers when a tab is activated.chrome.tabs.query({ active: true, currentWindow: true }, function (tabs) { ... })
: Queries the active tab in the current window.chrome.tabs.sendMessage(tabs[0].id, { action: 'SETTINGS_UPDATED' });
: Sends a message to the active tab's content script.
-
Options Script:
- React Component: Uses React hooks to manage settings state.
chrome.storage.sync.set
: Saves the updated settings.chrome.runtime.sendMessage({ action: 'SETTINGS_UPDATED' });
: Sends a message to notify that settings have been updated.
-
Content Script:
- Reducer Function: Manages state changes based on action types.
- useStore Hook:
useReducer
: Manages the state with the reducer.useEffect
: Listens for messages and updates settings whenSETTINGS_UPDATED
action is received.chrome.runtime.onMessage.addListener
: Listens for messages from the background script.chrome.storage.sync.get
: Fetches updated settings from Chrome's storage.
Conclusion
Using the chrome.tabs.onActivated
listener, you can effectively update the content script with the latest data whenever the user switches to a different tab. This approach ensures that your content script always has the most current data, providing a seamless user experience. The provided code example demonstrates how to implement this functionality in a Chrome extension.
To take your Chrome extension development to the next level, consider using Launchify. Launchify provides a comprehensive set of tools and resources to streamline your extension development process, making it easier to build, test, and deploy your Chrome extensions. Don't miss out on this opportunity to enhance your development workflow and create high-quality extensions. Purchase Launchify today and elevate your extension projects to new heights!