Skip to content

Instantly share code, notes, and snippets.

@marcelaraujo
Created September 6, 2021 10:29
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save marcelaraujo/005066fb0760d530acdd29c559933dd8 to your computer and use it in GitHub Desktop.
Save marcelaraujo/005066fb0760d530acdd29c559933dd8 to your computer and use it in GitHub Desktop.
React Tracking Elements DOM changes
    import { useState, useEffect, useRef } from "react";
    import "./styles.css";
    
    const weak = new WeakMap();
    
    let i = 0;
    const unique = () => i++;
    
    export default function App() {
      const ref = useRef(null);
      const [arr, setArr] = useState([0, 1, 2]);
    
      useEffect(() => {
        if (ref.current) {
          for (const child of [...ref.current.children]) {
            if (weak.has(child)) {
              // print its value
              console.log(
                "Seen: ",
                child,
                " assigned random number: ",
                weak.get(child)
              );
            } else {
              console.log(
                "New DOM element: ",
                child,
                " with value: ",
                child.textContent
              );
    
              weak.set(child, unique());
            }
          }
        }
      });
    
      return (
        <div className="App">
          <h1>Hello CodeSandbox</h1>
    
          <button onClick={() => setArr((prev) => [prev.length, ...prev])}>
            Action
          </button>
    
          <ul ref={ref}>
            {arr.map((key) => (
              <li key={key}>{key}</li>
            ))}
          </ul>
        </div>
      );
    }

By using a WeakMap and a HTMLCollection we can track each DOM element added by React. In this case assign an strictly increasing number to it. If we have seen it already, then just print out that it has been seen before, and the number assigned to it.

I am using the HTMLCollection by accessing the children property on the `ul` element. If you store this orthogonality to React, that means out of any life cycle bounds, you'll see this list changing because it is a live collection, and when the DOM mutates so does the HTMLCollection. It is not super essential for the snippet above, but I think you might want to explore further. A MutationObserver, a weakMap and the live HTMLCollection could be used to build a widget that shows the newly added DOM elements, presumably added by React.

Good question! You got a bunch of people surprised.

Here's a codesandbox to get started ASAP: https://codesandbox.io/s/currying-morning-m1oop

And some logs:

    // initial render
    New DOM element:  <li>​0</li>​  with value:  0
    New DOM element:  <li>​1​</li>​  with value:  1
    New DOM element:  <li>​2​</li>​  with value:  2
    // after clicking action button
    New DOM element:  <li>​3​</li>​  with value:  3
    Seen:  <li>​0​</li>​  assigned random number:  0
    Seen:  <li>​1​</li>​  assigned random number:  1
    Seen:  <li>​2​</li>​  assigned random number:  2
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment