I am working on a project which produces a Chrome extension. In a background page I have a function named checkingProcess. This function is executed when a new tab is opened or a tab is updated. (I tried to catch the change of URL here.)
chrome.tabs.onActivated.addListener((activeInfo) => {
checkingProcess(activeInfo.tabId)
})
chrome.tabs.onUpdated.addListener((tabId, changeInfo, tab) => {
checkingProcess(tab.id)
})
Then, in the checkingProcess function, I have some functions for the data handling and API calls. Then I tried to receive a message that comes from popup. This message represents that the popup was opened by the user.
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
if (request.msg === 'popup_opened') {
sendResponse({
matches_length: response['matches'].length,
hostname: host,
})
}
chrome.runtime.lastError
})
After that, it sends a response to the popup. In the popup, I listen to the message and use the response in the popup.
useEffect(() => {
chrome.runtime.sendMessage({ msg: 'popup_opened' }, (res) => {
setHostname(res['hostname'])
setMatchesLength(res['matches_length'])
console.log(res['hostname'], 'burası')
console.log(res['matches_length'], 'burası')
})
}, [])
However, I realize that this message process is only executed once, but I need to run it multiple times to access the data in background simultaneously. How can I do that?
Your message is only sent once because it's currently setup in a React.useEffect with an empty list of dependencies. This means this code will only be run once when your component is mounted. If you want to run it "multiple times" you first need to define what this means? Examples are:
Executing sendMessage after a user performs some action, like clicking a button. In that case you don't need useEffect. Instead, wire an event handler to that button and perform the sendMessage there.
Executing sendMessage after re-render of your component. Simply remove the empty list of dependencies ([]) from your useEffect method. Note: use this with caution. If you setup your component in a way that it re-renders often, this can quickly turn into a situation where many API calls are made.
Executing sendMessage after some state within your component changes. Add this variable to the list of dependencies: [loaded]
Executing sendMessage every 10 seconds. You'll want to use setInterval within your useEffect, like this:
useEffect(() => {
const interval = setInterval(() => {
chrome.runtime.sendMessage({ msg: 'popup_opened' }, (res) => {
setHostname(res['hostname'])
setMatchesLength(res['matches_length'])
console.log(res['hostname'], 'burası')
console.log(res['matches_length'], 'burası')
})
}, 10000);
return () => clearInterval(interval);
}, []);
Related
I have a chrome extension that stores data in Firestore and populates that data to the frontend. I always have to refresh the page to see newly added data, which isn’t a user friendly experience. How can I update the UI to show the newly updated data without having to refresh the page?
So far, I've tried using useEffect to get the data. Inside of it, I'm using a function that gets data from Firestore cached inside of chrome local storage.
Here is my code
const getFolderData = () => {
getDataFromChrome("docId").then((res: any) => {
setDocId(res.docId);
});
getDataFromChrome("content").then((res: any) => {
//console.log("getting in mainfolder",res);
// for (const item of res.content) {
// if (item.type.toLowerCase() === "subfolder") {
// // console.log(item)
// getSubFolder(item.id);
// }
// }
for (const item of res.content) {
setTiersContent((pre: any) => [...pre, item]);
}
});
};
useEffect(() => {
getFolderData();
}, []);
I also get this error. I'm also using the chrome extension API to communicate with a background script. It could be related to the problem
Uncaught (in promise) Error: A listener indicated an asynchronous response by returning true, but the message channel closed before a response was received
I've never used firebase so I'm not sure what your functions do, I can only guess. A few things wrong from what I can see:
Your useEffect is set to only run on page load since the dep array is empty, I assume you want to refetch on some condition.
If any of the 2 functions is supposed to be a subscription, your useEffect needs to return a cancel function.
Refetch data when needed is not a new problem, packages like React Query has tools that optimize your requests and refetch when needed. I suggest you give it a shot if your app has more than 2-3 fetch requests.
I am working on google extensions. I am trying to process message passing between my popup.tsx and background.ts
Before sending message from my background.ts, I print the data. Data exist. However, in my popup.tsx chrome.runtime.onMessage not triggered. I dont understand which function not working and what is the problem. I examine lots of example but result still the same. What is the problem? I am adding my message passing codes..
Here is my background.ts runtime.sendMessage func. this function is inside another function that is called 'collectData'. When I open new tab or update the tab, this function triggered and works. Everytime this data works stable. I can see the data in every page.
chrome.runtime.sendMessage({
msg: 'url_data',
data: data,
})
Then, I am trying to receive the data in my popup.tsx with runtime.onMessage. When I reset the extension, it works sometimes just once, then that onMessage func not working at all :
const [requestData, setRequestData] = useState<string>()
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
console.log(' popup runtime on message req', request)
if (request.msg === 'url_data') {
checkWebsite(request.data).then((res) => {
console.log('popup data alındı')
console.log('popu res', res)
setRequestData(res)
// chrome.runtime.sendMessage({
// msg: 'res_length',
// data: res.matches.length,
// })
// console.log('popup data res length gönderildi')
// res.matches.length === 0 ? setIsSecure(true) : setIsSecure(false)
setHost(request.data.hostname)
})
sendResponse({ message: 'res length sended' })
console.log('res length response send')
}
})
Everytime, I change request data state. If it changes, I want to update popup with useEffect.
useEffect(() => {
console.log('useeffect', requestData)
}, [requestData])
As a result, for the first the main problem is message passing. Why it is not working?
So for some reason, if I wanted to run a function to update a database when the user leaves a page, I can do that with a useEffect return/cleanup function. But is there a way to do that if they close the page/browser?
useEffect(() => {
return () => {
window.open("https://www.google.com", "_blank");
};
}, []);
I tried testing like so, but never seemed to work. So I am wondering if there is a way to do this.
To get notified when the window is just about to close, use the onbeforeunload event:
useEffect(() => {
const onBeforeUnload = (e) => {
// run your cleanup code here
}
window.addEventListener('beforeunload', onBeforeUnload);
return () => {
window.removeEventListener('beforeunload', onBeforeUnload);
}
}, []);
You are limited in terms of what you can do during this event. If you want, you can prompt the user whether they really want to close the page, which you do like this:
const onBeforeUnload = (e) => {
// Cancel the event
e.preventDefault(); // If you prevent default behavior in Mozilla Firefox prompt will always be shown
// Chrome requires returnValue to be set
e.returnValue = '';
}
This will display an alert-like message, which you cannot customize. Typically you should only do this if the user has done something where they would expect this kind of message. Ie, they've made changes, and those changes havn't been saved, and they'll be annoyed if they lose them. If you're just running cleanup code, you probably don't need to do this.
You cannot redirect them to another page or show a custom alert. Malicious pages would love this: "Ha ha, you tried to leave my page, well i'm routing you back to it instead".
I tried to call an unsubscribe function:
unsubscribeFromThingIds(arg.tids);
after the await cacheEntryRemoved; line in this example.
The function is defined like this:
export const unsubscribeFromThingIds = (tids: string[]) => {
followedThingIds = followedThingIds.filter(
id => !tids.includes(id)
);
const argument = { tids: followedThingIds } as ITimersChannelInitParams;
myIo!.emit("configure timers channel", argument);
};
(where myIo is the Socket.IO client)
This is not working currently for me. I am currently debugging this issue.
I also tried to use an useEffect hook with a returned cleanup function for running it when the component unmounts, something like this:
useEffect(() => {
return () => {
unsubscribeFromThingIds(getTimersQueryParams.tids);
};
}, [getTimersQueryParams]);
I want to ask for this information because I am not sure which one of the two ideas should I work on more, or if there is another thing I can do that is better.
Could it be that you are just impatient? The cache entry will be removed 60 seconds (the keepUnusedDataFor option) after the last component stops using it.
Generally: using the endpoint lifecycle and awaiting cacheEntryRemoved is the right way to do this if you started the listener within onCacheEntryAdded. If you want it to happen earlier, just use a shorter keepUnusedDataFor duration.
I'm trying to create a plugin for Figma, which has been going fine until now. It's based on the react example they provide on their github page: https://github.com/figma/plugin-samples/tree/master/react
In this example I've created a button, that on click will call this function:
(file: ui.tsx)
onClick = () => {
parent.postMessage({pluginMessage: {type: 'GetData'}}, '*');
};
This is parent.postMessage is a function figma provides to communicate with another file in the project, code.ts. This file will receive the postMessage with the pluginMessage as parameter, which works as expected. The code.ts that receives this looks like this:
(file: code.ts)
figma.ui.onmessage = msg => {
if (msg.type === 'GetData') {
figma.ui.postMessage({"title": figma.currentPage.selection[0].name});
}
};
This file receives the message, and it gets in the if statement since GetData has been set. Up until here, everything is good and well. The issue I'm walking into is the figma.ui.postMessage({}), which should do a callback to the onmessage function in ui.tsx:
(file: ui.tsx)
onmessage = (selection) => {
console.log(selection);
};
This onmessage function should, according to Figma's documentation, receive the object from the postMessage in code.ts. This does however never happen; it will never be called at all. I can't access the current selection in ui.tsx, so I need data from code.ts. Is there any way to pass this data to ui.tsx, or does anyone know why this doesn't work?
I encountered the same issue. Within your ui.tsx file, try adding the following:
window.onmessage = selection => {
let message = selection.data.pluginMessage;
console.log(message);
}
or try this ->
window.addEventListener("message", (selection) => {
console.log(selection);
});
this will add another message event handler to the window. if you use onmessage it might overwrite the previous handler!
put first of script
onmessage = event => {
console.log("got this from the plugin code", event, event.data.pluginMessage)
}