Reputation: 46
I've built a chrome extension using create-react-app to parse the current page's HTML and grab the number of relevant icons in that HTML.
I'm able to successfully build and run the extension on chrome, but I'm getting an error when I try to update state within a Chrome tab script.
Here's my code:
/*global chrome*/
import './App.css';
import React, {useState, useEffect} from 'react';
function App() {
let [poorAccess, setPA] = useState();
let [mediumAccess, setMA] = useState();
let [highAccess, setHA] = useState();
let [perfectAccess, setperfA] = useState();
let dispScripts = () => {
let theAccess = []
let poorAccesse = document.getElementsByClassName("ally-accessibility-score-indicator ally-accessibility-score-indicator-low ally-instructor-feedback");
let mediumAccesse = document.getElementsByClassName("ally-accessibility-score-indicator ally-accessibility-score-indicator-medium ally-instructor-feedback");
let highAccesse = document.getElementsByClassName("ally-accessibility-score-indicator ally-accessibility-score-indicator-high ally-instructor-feedback");
let perfectAccesse = document.getElementsByClassName("ally-accessibility-score-indicator ally-accessibility-score-indicator-perfect ally-instructor-feedback");
setPA(poorAccesse.length);
setMA(mediumAccesse.length);
setHA(highAccesse.length);
setperfA(perfectAccesse.length);
}
let grabScores = () => {
chrome.tabs.executeScript({code: `(${dispScripts})()`})
}
return (
<div className="App">
<button onClick={grabScores}>Parse</button>
</div>
);
}
export default App;
And it's giving me an error of:
VM3495:1 Uncaught ReferenceError: l is not defined
when I click my button.
So how would I go about updating state within a chrome script? Is this even possible?
Upvotes: 0
Views: 889
Reputation: 73506
executeScript
runs the code in the web page, which is a different page, so it doesn't have functions from your extension page such as setPA, setMA, and so on.
If they change the state of React app in the extension page/popup then split your injected code so it only returns the data in executeScript's callback. The data must be JSON-compatible: number, string, boolean, null, or an array/object that consists only of these types at any level of nesting.
// this runs in the web page as a content script
const dispScriptsStr = `(${() => {
return ['low', 'medium', 'high', 'perfect']
.map(s => document.getElementsByClassName(
'ally-accessibility-score-indicator' +
' ally-accessibility-score-indicator-' + s +
' ally-instructor-feedback').length);
}})()`;
// this runs in the extension page, including the callback
const grabScores = () => {
chrome.tabs.executeScript({code: dispScriptsStr}, results => {
if (!chrome.runtime.lastError) {
const res = results[0];
setPA(res[0]);
setMA(res[1]);
setHA(res[2]);
setperfA(res[3]);
}
});
};
But if you want to use these functions in the web page then you need to add them to the injected code string, of course.
Upvotes: 1
Reputation: 46
Found the answer:
Due to the async nature of the Chrome api, you have to do something like this:
let dispScripts = () => {
let theAccess = []
let poorAccesse = document.getElementsByClassName("ally-accessibility-score-indicator ally-accessibility-score-indicator-low ally-instructor-feedback");
let mediumAccesse = document.getElementsByClassName("ally-accessibility-score-indicator ally-accessibility-score-indicator-medium ally-instructor-feedback");
let highAccesse = document.getElementsByClassName("ally-accessibility-score-indicator ally-accessibility-score-indicator-high ally-instructor-feedback");
let perfectAccesse = document.getElementsByClassName("ally-accessibility-score-indicator ally-accessibility-score-indicator-perfect ally-instructor-feedback");
// setPA(poorAccesse.length);
// setMA(mediumAccesse.length);
// setHA(highAccesse.length);
// setperfA(perfectAccesse.length);
return poorAccesse.length;
}
and then send that return value here in a callback:
let grabScores = () => {
chrome.tabs.executeScript({code: `(${dispScripts})()`}, function(result){
setPA(result)
})
}
Upvotes: 0