Rerender child components when their parameters change

This commit is contained in:
Steve Sanderson 2018-01-26 14:25:26 -08:00
parent 94ad26a479
commit 882096755b
4 changed files with 53 additions and 3 deletions

View File

@ -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)

View File

@ -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>

View File

@ -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

View File

@ -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)