Skip to content

Fix dirtied callback firing on removed children#1890

Open
YonahKarp wants to merge 1 commit intofacebook:mainfrom
YonahKarp:export-D93010183
Open

Fix dirtied callback firing on removed children#1890
YonahKarp wants to merge 1 commit intofacebook:mainfrom
YonahKarp:export-D93010183

Conversation

@YonahKarp
Copy link

Summary:
As per Claude:

Root Cause

D92280506 added excludedChild->setDirty(true) to YGNodeRemoveChild and YGNodeRemoveAllChildren. The setDirty(true) call fires the dirtiedFunc_ callback, which causes a chain reaction that makes text disappear in React Native's legacy architecture.

The Bug Chain

  1. sizeThatFitsMinimumSize: (RCTShadowView:368-403) measures inline views by cloning their Yoga node via YGNodeClone. The clone inherits dirtiedFunc_ and context_ from the original.
  2. After measurement, YGNodeRemoveChild (line 391) removes the clone from the temp constraint node. The new setDirty(true) fires dirtiedFunc_ on the clone.
  3. The clone's dirtiedFunc_ is RCTInlineViewYogaNodeDirtied (RCTBaseTextShadowView:20-31), set when inline views are inserted into text nodes (line 59). The callback reads context_ from the clone — which points to the real inline view — gets its React superview (the text node), and calls [baseTextShadowView dirtyLayout].
  4. dirtyLayout (RCTTextShadowView:58-63) calls YGNodeMarkDirty(self.yogaNode), marking the text node dirty after Yoga had already cleared the dirty flag.
  5. When uiManagerWillPerformMounting runs (RCTTextShadowView:73-77), the guard if (YGNodeIsDirty(self.yogaNode)) { return; } bails out — text is
    never pushed to the native view.

Why setLayout({}) alone was sufficient

setLayout({}) resets generationCount to 0, which forces the Yoga layout algorithm to fully recalculate the node on the next layout pass (the needToVisitNode check in CalculateLayout.cpp:2167 compares generationCount against the current generation). The dirty flag is redundant for layout correctness but was intended as a semantic indicator.

Fix

Suppress the dirtiedFunc_ callback when marking removed children dirty. The dirty flag should still be set (for API correctness when checking YGNodeIsDirty), but the callback must not fire because the node is being detached from the tree.

Reviewed By: NickGerleman

Differential Revision: D93010183

Summary:
As per Claude:
## Root Cause

 D92280506 added excludedChild->setDirty(true) to YGNodeRemoveChild and YGNodeRemoveAllChildren. The setDirty(true) call fires the dirtiedFunc_ callback, which causes a chain reaction that makes text disappear in React Native's legacy architecture.

## The Bug Chain

   1. sizeThatFitsMinimumSize: (RCTShadowView:368-403) measures inline views by cloning their Yoga node via YGNodeClone. The clone inherits dirtiedFunc_ and context_ from the original.
   2. After measurement, YGNodeRemoveChild (line 391) removes the clone from the temp constraint node. The new setDirty(true) fires dirtiedFunc_ on the clone.
   3. The clone's dirtiedFunc_ is RCTInlineViewYogaNodeDirtied (RCTBaseTextShadowView:20-31), set when inline views are inserted into text nodes (line 59). The callback reads context_ from the clone — which points to the real inline view — gets its React superview (the text node), and calls [baseTextShadowView dirtyLayout].
   4. dirtyLayout (RCTTextShadowView:58-63) calls YGNodeMarkDirty(self.yogaNode), marking the text node dirty after Yoga had already cleared the dirty flag.
   5. When uiManagerWillPerformMounting runs (RCTTextShadowView:73-77), the guard if (YGNodeIsDirty(self.yogaNode)) { return; } bails out — text is
      never pushed to the native view.

 #### Why setLayout({}) alone was sufficient

 setLayout({}) resets generationCount to 0, which forces the Yoga layout algorithm to fully recalculate the node on the next layout pass (the needToVisitNode check in CalculateLayout.cpp:2167 compares generationCount against the current generation). The dirty flag is redundant for layout correctness but was intended as a semantic indicator.

 ## Fix

Suppress the dirtiedFunc_ callback when marking removed children dirty. The dirty flag should still be set (for API correctness when checking YGNodeIsDirty), but the callback must not fire because the node is being detached from the tree.

Reviewed By: NickGerleman

Differential Revision: D93010183
@vercel
Copy link

vercel bot commented Feb 24, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
yoga-website Ready Ready Preview, Comment Feb 24, 2026 2:05pm

Request Review

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant