Rerender child components when their parameters change
This commit is contained in:
parent
94ad26a479
commit
882096755b
|
|
@ -179,6 +179,7 @@ namespace Microsoft.AspNetCore.Blazor.RenderTree
|
|||
ref var newComponentNode = ref newTree[newComponentIndex];
|
||||
var componentId = oldComponentNode.ComponentId;
|
||||
var componentInstance = oldComponentNode.Component;
|
||||
var hasSetAnyProperty = false;
|
||||
|
||||
// Preserve the actual componentInstance
|
||||
newComponentNode.SetChildComponentInstance(componentId, componentInstance);
|
||||
|
|
@ -207,6 +208,7 @@ namespace Microsoft.AspNetCore.Blazor.RenderTree
|
|||
if (!Equals(oldPropertyValue, newPropertyValue))
|
||||
{
|
||||
SetChildComponentProperty(componentInstance, newName, newPropertyValue);
|
||||
hasSetAnyProperty = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
|
|
@ -216,6 +218,7 @@ namespace Microsoft.AspNetCore.Blazor.RenderTree
|
|||
// could consider removing the 'name equality' check entirely for perf
|
||||
SetChildComponentProperty(componentInstance, newName, newPropertyValue);
|
||||
RemoveChildComponentProperty(componentInstance, oldName);
|
||||
hasSetAnyProperty = true;
|
||||
}
|
||||
|
||||
oldStartIndex++;
|
||||
|
|
@ -235,6 +238,7 @@ namespace Microsoft.AspNetCore.Blazor.RenderTree
|
|||
SetChildComponentProperty(componentInstance,
|
||||
newTree[newStartIndex].AttributeName,
|
||||
newTree[newStartIndex].AttributeValue);
|
||||
hasSetAnyProperty = true;
|
||||
newStartIndex++;
|
||||
hasMoreNew = newEndIndexIncl >= newStartIndex;
|
||||
}
|
||||
|
|
@ -242,11 +246,19 @@ namespace Microsoft.AspNetCore.Blazor.RenderTree
|
|||
{
|
||||
RemoveChildComponentProperty(componentInstance,
|
||||
oldTree[oldStartIndex].AttributeName);
|
||||
hasSetAnyProperty = true;
|
||||
oldStartIndex++;
|
||||
hasMoreOld = oldEndIndexIncl >= oldStartIndex;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (hasSetAnyProperty)
|
||||
{
|
||||
// TODO: Instead, call some OnPropertiesUpdated method on IComponent,
|
||||
// whose default implementation causes itself to be rerendered
|
||||
_renderer.RenderComponent(componentId);
|
||||
}
|
||||
}
|
||||
|
||||
private static void RemoveChildComponentProperty(IComponent component, string componentPropertyName)
|
||||
|
|
|
|||
|
|
@ -55,7 +55,7 @@ namespace Microsoft.AspNetCore.Blazor.Rendering
|
|||
/// Updates the rendered state of the specified <see cref="IComponent"/>.
|
||||
/// </summary>
|
||||
/// <param name="componentId">The identifier of the <see cref="IComponent"/> to render.</param>
|
||||
protected void RenderComponent(int componentId)
|
||||
protected internal void RenderComponent(int componentId)
|
||||
=> GetRequiredComponentState(componentId).Render();
|
||||
|
||||
/// <summary>
|
||||
|
|
|
|||
|
|
@ -819,7 +819,8 @@ namespace Microsoft.AspNetCore.Blazor.Test
|
|||
private class FakeRenderer : Renderer
|
||||
{
|
||||
internal protected override void UpdateDisplay(int componentId, RenderTreeDiff renderTreeDiff)
|
||||
=> throw new NotImplementedException();
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
private class FakeComponent : IComponent
|
||||
|
|
@ -831,7 +832,8 @@ namespace Microsoft.AspNetCore.Blazor.Test
|
|||
private string PrivateProperty { get; set; }
|
||||
|
||||
public void BuildRenderTree(RenderTreeBuilder builder)
|
||||
=> throw new NotImplementedException();
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
private class FakeComponent2 : IComponent
|
||||
|
|
|
|||
|
|
@ -374,6 +374,42 @@ namespace Microsoft.AspNetCore.Blazor.Test
|
|||
Assert.Same(objectThatWillNotChange, updatedComponentInstance.ObjectProperty);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReRendersChildComponentsWhenPropertiesChange()
|
||||
{
|
||||
// Arrange: First render
|
||||
var renderer = new TestRenderer();
|
||||
var firstRender = true;
|
||||
var component = new TestComponent(builder =>
|
||||
{
|
||||
builder.OpenComponentElement<MessageComponent>(1);
|
||||
builder.AddAttribute(2, nameof(MessageComponent.Message), firstRender ? "first" : "second");
|
||||
builder.CloseElement();
|
||||
});
|
||||
|
||||
var rootComponentId = renderer.AssignComponentId(component);
|
||||
renderer.RenderComponent(rootComponentId);
|
||||
|
||||
var childComponentId = renderer.RenderTreesByComponentId[rootComponentId]
|
||||
.Single(node => node.NodeType == RenderTreeNodeType.Component)
|
||||
.ComponentId;
|
||||
|
||||
// This isn't strictly necessary for the test, but it's more common for components
|
||||
// to be updated after their first render than before it
|
||||
renderer.RenderComponent(childComponentId);
|
||||
|
||||
// Act: Second render
|
||||
firstRender = false;
|
||||
renderer.RenderComponent(rootComponentId);
|
||||
|
||||
var updatedComponentNode = renderer.RenderTreesByComponentId[rootComponentId]
|
||||
.Single(node => node.NodeType == RenderTreeNodeType.Component);
|
||||
|
||||
// Assert
|
||||
Assert.Collection(renderer.RenderTreesByComponentId[updatedComponentNode.ComponentId],
|
||||
node => AssertNode.Text(node, "second"));
|
||||
}
|
||||
|
||||
private class NoOpRenderer : Renderer
|
||||
{
|
||||
public new int AssignComponentId(IComponent component)
|
||||
|
|
|
|||
Loading…
Reference in New Issue