Skip to content

Commit f63e90e

Browse files
Omit omissible frames when updating rendertree to match bind event. Fixes #24014 (#26273)
1 parent 8d8d293 commit f63e90e

File tree

3 files changed

+35
-3
lines changed

3 files changed

+35
-3
lines changed

src/Components/Components/src/Rendering/RenderTreeBuilder.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -667,15 +667,16 @@ public void Clear()
667667

668668
// internal because this should only be used during the post-event tree patching logic
669669
// It's expensive because it involves copying all the subsequent memory in the array
670-
internal void InsertAttributeExpensive(int insertAtIndex, int sequence, string attributeName, object? attributeValue)
670+
internal bool InsertAttributeExpensive(int insertAtIndex, int sequence, string attributeName, object? attributeValue)
671671
{
672672
// Replicate the same attribute omission logic as used elsewhere
673673
if ((attributeValue == null) || (attributeValue is bool boolValue && !boolValue))
674674
{
675-
return;
675+
return false;
676676
}
677677

678678
_entries.InsertExpensive(insertAtIndex, RenderTreeFrame.Attribute(sequence, attributeName, attributeValue));
679+
return true;
679680
}
680681

681682
/// <summary>

src/Components/Components/src/Rendering/RenderTreeUpdater.cs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,14 @@ private static void UpdateFrameToMatchClientState(RenderTreeBuilder renderTreeBu
7575

7676
// If we get here, we didn't find the desired attribute, so we have to insert a new frame for it
7777
var insertAtIndex = elementFrameIndex + 1;
78-
renderTreeBuilder.InsertAttributeExpensive(insertAtIndex, RenderTreeDiffBuilder.SystemAddedAttributeSequenceNumber, attributeName, attributeValue);
78+
var didInsertFrame = renderTreeBuilder.InsertAttributeExpensive(insertAtIndex, RenderTreeDiffBuilder.SystemAddedAttributeSequenceNumber, attributeName, attributeValue);
79+
if (!didInsertFrame)
80+
{
81+
// The builder decided to omit the new frame, e.g., because it's a false-valued bool
82+
// In this case there's nothing else to update
83+
return;
84+
}
85+
7986
framesArray = renderTreeBuilder.GetFrames().Array; // Refresh in case it mutated due to the expansion
8087

8188
// Update subtree length for this and all ancestor containers

src/Components/Components/test/RenderTreeUpdaterTest.cs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,30 @@ public void AddsAttributeIfNotFound()
127127
frame => AssertFrame.Attribute(frame, "eventname", v => Assert.IsType<Action>(v), 1));
128128
}
129129

130+
[Fact]
131+
public void OmitsAttributeIfNotFoundButValueIsOmissible()
132+
{
133+
// Arrange
134+
var valuePropName = "testprop";
135+
var renderer = new TestRenderer();
136+
var builder = new RenderTreeBuilder();
137+
builder.OpenElement(0, "elem");
138+
builder.AddAttribute(1, "eventname", (Action)(() => { }));
139+
builder.SetUpdatesAttributeName(valuePropName);
140+
builder.CloseElement();
141+
var frames = builder.GetFrames();
142+
frames.Array[1] = frames.Array[1].WithAttributeEventHandlerId(123);
143+
144+
// Act
145+
RenderTreeUpdater.UpdateToMatchClientState(builder, 123, false);
146+
frames = builder.GetFrames();
147+
148+
// Assert
149+
Assert.Collection(frames.AsEnumerable(),
150+
frame => AssertFrame.Element(frame, "elem", 2, 0),
151+
frame => AssertFrame.Attribute(frame, "eventname", v => Assert.IsType<Action>(v), 1));
152+
}
153+
130154
[Fact]
131155
public void ExpandsAllAncestorsWhenAddingAttribute()
132156
{

0 commit comments

Comments
 (0)