React useMutationObserver hook
JavaScript's MutationObserver
API is a modern way to watch for changes made to the DOM tree. However, its imperative nature can make it difficult to use in a declarative manner. You can create a custom hook to make it easier to use MutationObserver
in React components.
All you really need is a ref
to the element you want to watch, a callback
function to be called when changes are detected, and an options
object to specify what changes to watch for.
To create your custom hook, you'll use the useEffect()
hook to set up the MutationObserver
and the ref
to watch. The effect will depend on the callback
and options
values.
Then, inside the effect callback, you'll check if the ref
is initialized. If it is, you'll create a new MutationObserver
and pass it the callback
. You'll then call MutationObserver.observe()
with the given options
to watch the ref
for changes.
Finally, when the component unmounts, you'll use the MutationObserver.disconnect()
method to remove the observer.
const useMutationObserver = ( ref, callback, options = { attributes: true, characterData: true, childList: true, subtree: true, } ) => { React.useEffect(() => { if (ref.current) { const observer = new MutationObserver(callback); observer.observe(ref.current, options); return () => observer.disconnect(); } }, [callback, options]); }; const App = () => { const mutationRef = React.useRef(); const [mutationCount, setMutationCount] = React.useState(0); const incrementMutationCount = () => { return setMutationCount(mutationCount + 1); }; useMutationObserver(mutationRef, incrementMutationCount); const [content, setContent] = React.useState('Hello world'); return ( <> <label for="content-input">Edit this to update the text:</label> <textarea id="content-input" style={{ width: '100%' }} value={content} onChange={e => setContent(e.target.value)} /> <div style={{ width: '100%' }} ref={mutationRef} > <div style={{ resize: 'both', overflow: 'auto', maxWidth: '100%', border: '1px solid black', }} > <h2>Resize or change the content:</h2> <p>{content}</p> </div> </div> <div> <h3>Mutation count {mutationCount}</h3> </div> </> ); }; ReactDOM.createRoot(document.getElementById('root')).render( <App /> );