Skip to content

Home

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 />
);

More like this

Start typing a keyphrase to see matching snippets.