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];
|
ref var newComponentNode = ref newTree[newComponentIndex];
|
||||||
var componentId = oldComponentNode.ComponentId;
|
var componentId = oldComponentNode.ComponentId;
|
||||||
var componentInstance = oldComponentNode.Component;
|
var componentInstance = oldComponentNode.Component;
|
||||||
|
var hasSetAnyProperty = false;
|
||||||
|
|
||||||
// Preserve the actual componentInstance
|
// Preserve the actual componentInstance
|
||||||
newComponentNode.SetChildComponentInstance(componentId, componentInstance);
|
newComponentNode.SetChildComponentInstance(componentId, componentInstance);
|
||||||
|
|
@ -207,6 +208,7 @@ namespace Microsoft.AspNetCore.Blazor.RenderTree
|
||||||
if (!Equals(oldPropertyValue, newPropertyValue))
|
if (!Equals(oldPropertyValue, newPropertyValue))
|
||||||
{
|
{
|
||||||
SetChildComponentProperty(componentInstance, newName, newPropertyValue);
|
SetChildComponentProperty(componentInstance, newName, newPropertyValue);
|
||||||
|
hasSetAnyProperty = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|
@ -216,6 +218,7 @@ namespace Microsoft.AspNetCore.Blazor.RenderTree
|
||||||
// could consider removing the 'name equality' check entirely for perf
|
// could consider removing the 'name equality' check entirely for perf
|
||||||
SetChildComponentProperty(componentInstance, newName, newPropertyValue);
|
SetChildComponentProperty(componentInstance, newName, newPropertyValue);
|
||||||
RemoveChildComponentProperty(componentInstance, oldName);
|
RemoveChildComponentProperty(componentInstance, oldName);
|
||||||
|
hasSetAnyProperty = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
oldStartIndex++;
|
oldStartIndex++;
|
||||||
|
|
@ -235,6 +238,7 @@ namespace Microsoft.AspNetCore.Blazor.RenderTree
|
||||||
SetChildComponentProperty(componentInstance,
|
SetChildComponentProperty(componentInstance,
|
||||||
newTree[newStartIndex].AttributeName,
|
newTree[newStartIndex].AttributeName,
|
||||||
newTree[newStartIndex].AttributeValue);
|
newTree[newStartIndex].AttributeValue);
|
||||||
|
hasSetAnyProperty = true;
|
||||||
newStartIndex++;
|
newStartIndex++;
|
||||||
hasMoreNew = newEndIndexIncl >= newStartIndex;
|
hasMoreNew = newEndIndexIncl >= newStartIndex;
|
||||||
}
|
}
|
||||||
|
|
@ -242,11 +246,19 @@ namespace Microsoft.AspNetCore.Blazor.RenderTree
|
||||||
{
|
{
|
||||||
RemoveChildComponentProperty(componentInstance,
|
RemoveChildComponentProperty(componentInstance,
|
||||||
oldTree[oldStartIndex].AttributeName);
|
oldTree[oldStartIndex].AttributeName);
|
||||||
|
hasSetAnyProperty = true;
|
||||||
oldStartIndex++;
|
oldStartIndex++;
|
||||||
hasMoreOld = oldEndIndexIncl >= 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)
|
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"/>.
|
/// Updates the rendered state of the specified <see cref="IComponent"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="componentId">The identifier of the <see cref="IComponent"/> to render.</param>
|
/// <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();
|
=> GetRequiredComponentState(componentId).Render();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
|
||||||
|
|
@ -819,7 +819,8 @@ namespace Microsoft.AspNetCore.Blazor.Test
|
||||||
private class FakeRenderer : Renderer
|
private class FakeRenderer : Renderer
|
||||||
{
|
{
|
||||||
internal protected override void UpdateDisplay(int componentId, RenderTreeDiff renderTreeDiff)
|
internal protected override void UpdateDisplay(int componentId, RenderTreeDiff renderTreeDiff)
|
||||||
=> throw new NotImplementedException();
|
{
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class FakeComponent : IComponent
|
private class FakeComponent : IComponent
|
||||||
|
|
@ -831,7 +832,8 @@ namespace Microsoft.AspNetCore.Blazor.Test
|
||||||
private string PrivateProperty { get; set; }
|
private string PrivateProperty { get; set; }
|
||||||
|
|
||||||
public void BuildRenderTree(RenderTreeBuilder builder)
|
public void BuildRenderTree(RenderTreeBuilder builder)
|
||||||
=> throw new NotImplementedException();
|
{
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class FakeComponent2 : IComponent
|
private class FakeComponent2 : IComponent
|
||||||
|
|
|
||||||
|
|
@ -374,6 +374,42 @@ namespace Microsoft.AspNetCore.Blazor.Test
|
||||||
Assert.Same(objectThatWillNotChange, updatedComponentInstance.ObjectProperty);
|
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
|
private class NoOpRenderer : Renderer
|
||||||
{
|
{
|
||||||
public new int AssignComponentId(IComponent component)
|
public new int AssignComponentId(IComponent component)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue