DEV Community

Rache4
Rache4

Posted on

1

Unexpected Behavior of React DOM Modification

Hello developers 🙂

I need help in understanding a piece of code that I developed recently.
My goal is to display a full binary tree (0 or 2 children allowed) which gives the user the ability to interact only with the leaves. The user can either split or remove a specific leaf.

  1. Upon split: set the splitted node as the left child of a new parent node. Example - split(1)
  2. Upon remove: find the node's parent and replace it with node's sibling subtree. Example - remove(3)

Implementation details:

Node class

Represents the structure of the tree and supports:

  • split()
  • remove()
let id = 0

class Node {
  constructor(parent, children){
    this.id = id++
    this.children = children? children : null
    this.parent = parent? parent : null
 }

  split(){
    const node = new Node(this.parent)
    node.children = [this, new Node(node)]
    this.parent = node
    if (node.parent) {
      // Replace parent's child (this) with node
    }
    return node
  }

  remove(){
    const parent = this.parent
    if (!parent) {
        return this
    }
    const sibling = parent.children.find((child) => child !== this)
    sibling.parent = parent.parent
    if (parent.parent) {
      // Replace grandparent's child (this.parent) with sibling
    }
    return sibling
  }
}
Enter fullscreen mode Exit fullscreen mode

TreeNode

Recursive component which contains node as a state
and renders the node's subtree.

function TreeNode(props) {
  const [node, setNode] = useState(props.node)

  useEffect(() => {
    setNode(props.node)
    return () => {  
    };
  }, [props.node]);

  const onRemove = () => {
      const newNode = node.remove()
      props.onRemove(newNode)
  }

  const onSplit = () => {
      setNode(node.split())
  }

  return (
    <div>
      {
        node.children? 
        <div>
          <label>{node.id}</label>
          <div>
            <TreeNode node={node.children[0]} onRemove={setNode}/>
            <TreeNode node={node.children[1]} onRemove={setNode}/>
          </div>
        </div>: 
        <div>
          <button onClick={onRemove}>remove</button>
          <button onClick={onSplit}>split</button>
          <label>{node.id}</label>
      </div>
      }

    </div>
  )
}
Enter fullscreen mode Exit fullscreen mode

The problem

Consider the tree I showed above (example - remove(3)), the actual result is:
Actual result - remove(3)

Although the tree structure is correct.

So here is my question - why React doesn't change the DOM as I expected (as I showed in the above)?

I also noticed that React does change the DOM as I expected for this case:
Actual result - remove(1)

Here is a full example in Sandbox: https://codesandbox.io/embed/full-binary-tree-react-21r77

Thanks in advance 🙏

Sentry blog image

How I fixed 20 seconds of lag for every user in just 20 minutes.

Our AI agent was running 10-20 seconds slower than it should, impacting both our own developers and our early adopters. See how I used Sentry Profiling to fix it in record time.

Read more

Top comments (1)

Collapse
 
wclayferguson profile image
Clay Ferguson

Your onRemove never updates the state right? Also useEffect normally should be using an existing state and not setting state.

Billboard image

Create up to 10 Postgres Databases on Neon's free plan.

If you're starting a new project, Neon has got your databases covered. No credit cards. No trials. No getting in your way.

Try Neon for Free →

👋 Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay