PlasmoHQ / plasmo

🧩 The Browser Extension Framework
https://www.plasmo.com
MIT License
10.31k stars 358 forks source link

[BUG] Infinite loop rendering occurs when using getInlineAnchorList() and getRootContainer() #1008

Closed Bedilatory closed 2 months ago

Bedilatory commented 3 months ago

What happened?

When I use these two methods, the anchor on the page will be infinitely mounted nodes

image image

My current solution is to filter the list returned by getInlineAnchorList. I'm not sure if this method is correct, but it does solve the problem of infinite loop rendering. Can you tell us if there is a better way?

image

Version

Latest

What OS are you seeing the problem on?

MacOSX

What browsers are you seeing the problem on?

Chrome, Microsoft Edge

Relevant log output

No response

(OPTIONAL) Contribution

Code of Conduct

Srkl commented 2 months ago

After losing a few hours on the same issue, I found the cause and a solution.

In summary, when we use document.createElement Plasmo is not aware of the DOM change. The documentation assumes that we are using an existing DOM element as the rootContainer, which doesn't work for dynamically created elements.

To fix the issue, we have to add the new Element to the hostSet and hostMap via the mountState variable.

export const getRootContainer = ({ anchor, mountState }) =>
  new Promise((resolve) => {
    const checkInterval = setInterval(() => {
      let { element, insertPosition } = anchor
      if (element) {
        const rootContainer = document.createElement("span")

        mountState.hostSet.add(rootContainer)
        mountState.hostMap.set(rootContainer, anchor)

        element.insertAdjacentElement(insertPosition, rootContainer)

        clearInterval(checkInterval)
        resolve(rootContainer)
      }
    }, 137)
  })
Bedilatory commented 2 months ago

After losing a few hours on the same issue, I found the cause and a solution.

In summary, when we use document.createElement Plasmo is not aware of the DOM change. The documentation assumes that we are using an existing DOM element as the rootContainer, which doesn't work for dynamically created elements.

To fix the issue, we have to add the new Element to the hostSet and hostMap via the mountState variable.

export const getRootContainer = ({ anchor, mountState }) =>
  new Promise((resolve) => {
    const checkInterval = setInterval(() => {
      let { element, insertPosition } = anchor
      if (element) {
        const rootContainer = document.createElement("span")

        mountState.hostSet.add(rootContainer)
        mountState.hostMap.set(rootContainer, anchor)

        element.insertAdjacentElement(insertPosition, rootContainer)

        clearInterval(checkInterval)
        resolve(rootContainer)
      }
    }, 137)
  })

OK, your method worked, thank you very much for your help. I had noticed the mountState property before, but I didn't find a way to use it, which led me to find another way. Maybe you can suggest to the author to write your way of using it in the document, it would be very helpful for other users! Anyway, thanks a lot for your help, I think your way should be the final solution!