简体   繁体   中英

ReactJS - Detect third-party DOM insert using Mutation Observer

Inside the component, a third-party JavaScript library has injected some content into a specific DIV ( voice-service ) after React render. I wanted to add or remove some CSS class on that DIV ( voice-service ) when the DOM was injected using Mutation Observer, like -

When DOM injected -

<div id="maxVoiceContainer" class="voice-service service--active">
 // third-party content
</div>

When DOM Not injected -

<div id="maxVoiceContainer" class="voice-service service--inactive">
// third-party content
</div>

heroVoice Component:

import React, { useRef, useState, useEffect } from 'react';

const heroVoice = (props) => {

    const elementRef = useRef();
    const [voiceAvailability, setVoiceAvailability] = useState(false);

    useEffect(() => {
        const config = { attributes: false, characterData: false, childList: true, subtree: true, attributeOldValue: false, characterDataOldValue: false };
        const observer = new MutationObserver(mutations => {
            mutations.forEach(mutation => {
                const newNodes = mutation.addedNodes;
                newNodes.forEach(node => {
                    if (node.classList && node.classList.contains('pocket-sphinx-container')) {
                        setVoiceAvailability(status => !status);
                    }
                });
            });
        });
        observer.observe(elementRef.current, config);
    }, []);

    return (
        <div
            ref={elementRef}
            id="maxVoiceContainer"
            className={`voice-service ${voiceAvailability ? ' service--active' : 'service--inactive'}`}
        >
          // third-party content
        </div>
    );
}

export default heroVoice;

Wanted to know -

  1. Do I need to disconnect the observer?
  2. Looks good? best practices?
  3. Also, I do not want to re-render when the state was updated!

Q: Do I need to disconnect the observer? It's not needed directly, but I recommand it with the mutationObserver.disconnect() Method, because you is called One time on ComponentDidMount. An empty Array at the second parameter means it. Ref

Looks good? best practices? Yes looks quite good. Avoid nested forEach. Better use first a map and than an forEach

 mutations.map(mutation => mutation.addedNodes).forEach(node => { if (node.classList && node.classList.contains('pocket-sphinx-container')) { setVoiceAvailability(status => !status); } });

Also, I do not want to re-render when the state was updated! React's lifecycle is build for this. There are two possibilities. First, you can use Redux or you make a local variable which is being setted in the forEach. But generally don't use a setState hook in a forEach

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM