基于用户导航在Chrome扩展中更新注入DOM的Reaction组件 [英] Update React component injected to DOM in Chrome Extension based on user navigation

查看:26
本文介绍了基于用户导航在Chrome扩展中更新注入DOM的Reaction组件的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在我的Chrome扩展中有一个简单的Reaction组件作为内容脚本注入到DOM中,运行在一个不是我的网站上。

我要更新(重新呈现)用户在我的Reaction应用注入到的网站上所做的每个导航(URL更改)上的组件。
我认为Reaction路由器应该是这项工作的最佳工具。
然而,它似乎并没有奏效。 我发现了一些相关问题:thisthisthis,但这些解决方案都不适合我。

我尝试将唯一的key添加到Route,(就像使用useLocation或简单地使用location.href或甚至Math.random()进行测试)-但这些都不会使组件更新。

奇怪的是,如果我附加一个onclick将组件中的状态更改为div,它确实会成功地使组件重新呈现和更新。

我的内容脚本如下所示:

import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';

const myDiv = document.createElement('div');
myDiv.id = 'my-id';
document.body.appendChild(myDiv);

ReactDOM.render(<App />, myDiv);

我的路由(Reaction-路由器v6):

import {
    BrowserRouter,
    Routes,
    Route,
    useParams,
    useLocation
} from "react-router-dom";

function App() {
    return (
        <BrowserRouter>
            <Routes>
                <Route path="/user/:id/" element={<MyComponent />} />
            </Routes>
        </BrowserRouter>
    );
}

我的组件:

function MyComponent(props) {
    const { id } = useParams();

    React.useEffect(() => {
       // Some stuff
    }, [id])

    return <div> User {id}</div>

我的manifest.json脚本如下:

  "background": {
    "scripts": [
      "build/background.js"
    ]
  },
  "content_scripts": [
    {
      "matches": [
        "https://example-website.com/*"
      ],
      "js": [
        "build/content.js"
      ],
      "css": [
        "build/css/content.css"
      ]
    },
    {
      "matches": [
        "https://example-website.com/*"
      ],
      "js": [
        "build/reactApp.js"
      ],
      "css": [
        "build/css/content.css"
      ]
    }
  ],

推荐答案

我要更新(重新呈现)用户在我的Reaction应用注入到的网站上所做的每个导航(URL更改)的组件。

您的问题的答案完全取决于每个导航(url更改)在代码方面的确切定义。听起来您对此并不十分确定(但如果您是,请使用此信息更新问题)。

当匹配的URL发生页面重新加载事件时,您的应用程序已再次注入,因此涵盖范围。

如果站点是单页面应用程序,并且正在修改其内容而不重新加载页面(例如,Reaction),则取决于视图数据(和URL)更改时发生的情况。

有一些窗口事件可以监听并重新呈现以响应:hashchangepopstate。性能最差的解决方案是简单地投票:如果没有其他方法奏效,这是你最后的选择。

以下是如何更改您的内容脚本:

import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';

const myDiv = document.createElement('div');
myDiv.id = 'my-id';
document.body.appendChild(myDiv);

const render = () => ReactDOM.render(<App />, myDiv);

render();

const handleLocationUpdates = () => {
  window.addEventListener("hashchange", render);
  window.addEventListener("popstate", render);

  // or, if neither of those work, you might have to poll:

  // const aDelayValueThatWorksForYou = 500;
  // setInterval(render, aDelayValueThatWorksForYou);
};

handleLocationUpdates();

注意:要获得ID,您需要使用Reaction路由器中的useParams钩子解析URL的一部分。我从来没有测试过使用在路由器组件本身之上的反应路由器来重新呈现应用程序。我的猜测是,它会订阅这样的重新渲染的更改,但我不知道这种行为。否则,您需要在应用程序中手动解析window.location.pathname中的ID,而不是使用useParams挂钩。


编辑

因为您尚未共享要向其中注入扩展的站点的URL(为了重现该问题),所以我创建了一个自包含的示例,您可以在本地Web服务器上运行该示例以查看其工作情况:

引用:MDN: Proxy/handler.apply()

example.html

<!doctype html>
<head>
  <meta name="viewport" content="width=device-width, initial-scale=1" />
  <title>Example</title>

  <script type="module">
    // Imagine that this is the JS in the example website (that you don't control)
    const delay = (ms) => new Promise(res => setTimeout(res, ms));
    const updateUrl = (relativePath) => window.history.pushState({}, '', new URL(relativePath, window.location.href));

    await delay(1500);
    updateUrl('/user/2');

    await delay(1500);
    updateUrl('/about');

    await delay(1500);
    updateUrl('/user/3');

  </script>

  <!-- This examples uses UMD modules instead of ESM or bundling -->
  <script src="https://unpkg.com/react@17.0.2/umd/react.development.js"></script><script src="https://unpkg.com/react-dom@17.0.2/umd/react-dom.development.js"></script><script src="https://unpkg.com/@babel/standalone@7.17.2/babel.min.js"></script>
  <script type="text/babel" data-type="module" data-presets="env,react">
    // This is your entire React app

    const {useEffect, useState} = React;

    function parseId () {
      const idRegex = /^/user/(?<id>[^?/#]+)/;
      const id = window.location.pathname.match(idRegex)?.groups?.id;
      return id;
    }

    function useId () {
      const [id, setId] = useState();

      useEffect(() => {
        const parsed = parseId();
        if (parsed !== id) setId(parsed);
      });

      return id;
    }

    function IDComponent () {
      const id = useId();

      useEffect(() => {
        if (!id) return; // No ID in current location
        console.log('New ID:', id);
      }, [id]);

      return id ?
        (<div>User ID: {id}</div>)
        : (<div>User ID not found</div>);
    }

    function App () {
      return (
        <div>
          <h1>Detecting ID changes...</h1>
          <IDComponent />
        </div>
      );
    }

    // The logic of the body of this function would be in your content script
    function main () {
      const reactRoot = document.body.appendChild(document.createElement('div'));
      const render = () => ReactDOM.render(<App />, reactRoot);

      const applyCallback = (object, method, callback) => {
        object[method] = new Proxy(object[method], {
          apply: (target, thisArg, args) => {
            callback();
            return target.apply(thisArg, args);
          },
        });
      };

      const handleLocationUpdates = (usePolling) => {
        if (usePolling) {
          const aDelayValueThatWorksForYou = 500;
          setInterval(render, aDelayValueThatWorksForYou);
          return;
        }
        window.addEventListener('hashchange', render);
        window.addEventListener('popstate', render);
        applyCallback(window.history, 'pushState', render);
        applyCallback(window.history, 'replaceState', render);
      };

      render();

      // If the event listeners and proxy interceptor don't handle the
      // "navigation" events created by the page you're injecting into,
      // then you'll need to set the polling option to `true`
      handleLocationUpdates(false);
    }

    main();
  </script>
</head>

<body></body>
</html>

这篇关于基于用户导航在Chrome扩展中更新注入DOM的Reaction组件的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆