loading...

Unexpected Behavior of React DOM Modification

rache4 profile image Rache4 ・2 min read

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 🙏

Discussion

pic
Editor guide
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.