Search code examples
javascriptreactjsgoogle-chromegoogle-chrome-extension

Update State within Chrome Extension React app


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?


Solution

  • 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.