Hone logo
Hone
Problems

React Mutation Observer Hook Implementation

This challenge asks you to implement a custom React hook, useMutationObserver, that leverages the browser's MutationObserver API. MutationObserver allows you to observe changes to the DOM and react to them. This hook will encapsulate the setup and teardown of a MutationObserver, providing a convenient way to monitor specific DOM elements for modifications within your React components.

Problem Description

You need to create a useMutationObserver hook that takes a ref to a DOM element and an optional callback function as arguments. The hook should:

  1. Initialize a MutationObserver: When the component mounts, the hook should create a new MutationObserver instance.
  2. Configure the Observer: The observer should be configured to monitor changes to the element's attributes, child nodes, and character data. You can use childList, subtree, attributes, characterData, and attributeFilter options. For simplicity, initially monitor all attributes.
  3. Attach the Observer: The observer should be attached to the DOM element referenced by the provided ref.
  4. Execute the Callback: When a mutation occurs, the observer's callback function should be executed. This callback should receive the mutations as an argument.
  5. Clean Up: When the component unmounts, the observer should be disconnected to prevent memory leaks.
  6. Handle Missing Element: If the ref is initially null or the element is removed from the DOM before the observer is attached, the hook should gracefully handle this situation without throwing errors.

Key Requirements:

  • The hook must be written in TypeScript.
  • The hook must correctly set up and tear down the MutationObserver.
  • The callback function must be executed with the correct mutation data.
  • The hook must handle cases where the DOM element is not immediately available.
  • The hook should not cause memory leaks.

Expected Behavior:

When the component mounts, the hook should start observing the DOM element. Any changes to the element's attributes, child nodes, or character data should trigger the callback function. When the component unmounts, the observer should be disconnected.

Edge Cases to Consider:

  • The ref being initially null.
  • The DOM element being removed from the DOM before the observer is attached.
  • The DOM element being dynamically added to the DOM after the component mounts.
  • Multiple components using the same ref.

Examples

Example 1:

Input: A component with a ref to a div element, and a callback function that logs the mutations to the console. The div element's text content is changed after the component mounts.
Output: The callback function is executed with the mutation data, and the mutations are logged to the console.
Explanation: The MutationObserver detects the change to the div element's text content and triggers the callback.

Example 2:

Input: A component with a ref to a div element, and a callback function that updates the component's state with the new attribute value. The div element's class attribute is changed after the component mounts.
Output: The component's state is updated with the new class attribute value.
Explanation: The MutationObserver detects the change to the div element's class attribute and triggers the callback, which updates the component's state.

Example 3: (Edge Case)

Input: A component with a ref to a div element that is initially not in the DOM. The div element is added to the DOM after the component mounts.
Output: The MutationObserver is attached to the div element when it is added to the DOM, and any subsequent changes to the element are detected.
Explanation: The hook correctly handles the dynamic addition of the DOM element to the DOM.

Constraints

  • The hook must be compatible with React 18 or later.
  • The callback function should be invoked asynchronously to avoid blocking the main thread.
  • The hook should be performant and avoid unnecessary re-renders.
  • The observer should only be attached to the element referenced by the ref.

Notes

  • Consider using useEffect to manage the observer's lifecycle.
  • The attributeFilter option can be used to specify which attributes to monitor. For this challenge, you can start with monitoring all attributes.
  • Be mindful of memory leaks when working with MutationObserver. Always disconnect the observer when the component unmounts.
  • Think about how to handle cases where the ref is not immediately available. You might want to use a conditional check before attaching the observer.
  • The callback function will receive a list of MutationRecord objects. Refer to the MDN documentation for details on the MutationRecord interface: https://developer.mozilla.org/en-US/docs/Web/API/MutationRecord
Loading editor...
typescript