Preserve child component instances across renders. Move instantiation into the diffing phase.
This commit is contained in:
parent
23c2816bcd
commit
2469558410
|
|
@ -2,14 +2,28 @@
|
|||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using Microsoft.Blazor.Components;
|
||||
using Microsoft.Blazor.Rendering;
|
||||
|
||||
namespace Microsoft.Blazor.RenderTree
|
||||
{
|
||||
internal class RenderTreeDiffComputer
|
||||
{
|
||||
private readonly Renderer _renderer;
|
||||
private readonly ArrayBuilder<RenderTreeEdit> _entries = new ArrayBuilder<RenderTreeEdit>(10);
|
||||
|
||||
public RenderTreeDiff ComputeDifference(
|
||||
public RenderTreeDiffComputer(Renderer renderer)
|
||||
{
|
||||
_renderer = renderer ?? throw new ArgumentNullException(nameof(renderer));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// As well as computing the diff between the two trees, this method also has the side-effect
|
||||
/// of instantiating child components on newly-inserted Component nodes, and copying the existing
|
||||
/// component instances onto retained Component nodes. It's particularly convenient to do that
|
||||
/// here because we have the right information and are already walking the trees to do the diff.
|
||||
/// </summary>
|
||||
public RenderTreeDiff ApplyNewRenderTreeVersion(
|
||||
ArrayRange<RenderTreeNode> oldTree,
|
||||
ArrayRange<RenderTreeNode> newTree)
|
||||
{
|
||||
|
|
@ -111,13 +125,19 @@ namespace Microsoft.Blazor.RenderTree
|
|||
|
||||
if (treatAsInsert)
|
||||
{
|
||||
if (newTree[newStartIndex].NodeType == RenderTreeNodeType.Attribute)
|
||||
var newNodeType = newTree[newStartIndex].NodeType;
|
||||
if (newNodeType == RenderTreeNodeType.Attribute)
|
||||
{
|
||||
Append(RenderTreeEdit.SetAttribute(siblingIndex, newStartIndex));
|
||||
newStartIndex++;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (newNodeType == RenderTreeNodeType.Element || newNodeType == RenderTreeNodeType.Component)
|
||||
{
|
||||
InstantiateChildComponents(newTree, newStartIndex);
|
||||
}
|
||||
|
||||
Append(RenderTreeEdit.PrependNode(siblingIndex, newStartIndex));
|
||||
newStartIndex = NextSiblingIndex(newTree, newStartIndex);
|
||||
siblingIndex++;
|
||||
|
|
@ -225,10 +245,15 @@ namespace Microsoft.Blazor.RenderTree
|
|||
var newComponentType = newTree[newNodeIndex].ComponentType;
|
||||
if (oldComponentType == newComponentType)
|
||||
{
|
||||
// Since it's the same child component type, we'll preserve the instance
|
||||
// rather than instantiating a new one
|
||||
newTree[newNodeIndex].SetChildComponentInstance(
|
||||
oldTree[oldNodeIndex].ComponentId,
|
||||
oldTree[oldNodeIndex].Component);
|
||||
|
||||
// TODO: Compare attributes and notify the existing child component
|
||||
// instance of any changes
|
||||
// TODO: Also copy the existing child component instance to the new
|
||||
// tree so we can preserve it across multiple updates
|
||||
|
||||
siblingIndex++;
|
||||
}
|
||||
else
|
||||
|
|
@ -301,5 +326,22 @@ namespace Microsoft.Blazor.RenderTree
|
|||
|
||||
_entries.Append(entry);
|
||||
}
|
||||
|
||||
private void InstantiateChildComponents(RenderTreeNode[] nodes, int startIndex)
|
||||
{
|
||||
var endIndex = nodes[startIndex].ElementDescendantsEndIndex;
|
||||
for (var i = startIndex; i <= endIndex; i++)
|
||||
{
|
||||
if (nodes[i].NodeType == RenderTreeNodeType.Component)
|
||||
{
|
||||
if (nodes[i].Component != null)
|
||||
{
|
||||
throw new InvalidOperationException($"Child component already exists during {nameof(InstantiateChildComponents)}");
|
||||
}
|
||||
|
||||
_renderer.InstantiateChildComponent(ref nodes[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ namespace Microsoft.Blazor.Rendering
|
|||
_componentId = componentId;
|
||||
_component = component ?? throw new ArgumentNullException(nameof(component));
|
||||
_renderer = renderer ?? throw new ArgumentNullException(nameof(renderer));
|
||||
_diffComputer = new RenderTreeDiffComputer();
|
||||
_diffComputer = new RenderTreeDiffComputer(renderer);
|
||||
_renderTreeBuilderCurrent = new RenderTreeBuilder(renderer);
|
||||
_renderTreeBuilderPrevious = new RenderTreeBuilder(renderer);
|
||||
}
|
||||
|
|
@ -48,30 +48,12 @@ namespace Microsoft.Blazor.Rendering
|
|||
|
||||
_renderTreeBuilderCurrent.Clear();
|
||||
_component.BuildRenderTree(_renderTreeBuilderCurrent);
|
||||
var diff = _diffComputer.ComputeDifference(
|
||||
var diff = _diffComputer.ApplyNewRenderTreeVersion(
|
||||
_renderTreeBuilderPrevious.GetNodes(),
|
||||
_renderTreeBuilderCurrent.GetNodes());
|
||||
EnsureChildComponentsInstantiated(diff.CurrentState); // TODO: Move this into the diff phase
|
||||
_renderer.UpdateDisplay(_componentId, diff);
|
||||
}
|
||||
|
||||
private void EnsureChildComponentsInstantiated(ArrayRange<RenderTreeNode> renderTree)
|
||||
{
|
||||
var array = renderTree.Array;
|
||||
var count = renderTree.Count;
|
||||
for (var i = 0; i < count; i++)
|
||||
{
|
||||
if (array[i].NodeType == RenderTreeNodeType.Component
|
||||
&& array[i].Component == null)
|
||||
{
|
||||
var instance = (IComponent)Activator.CreateInstance(array[i].ComponentType);
|
||||
array[i].SetChildComponentInstance(
|
||||
_renderer.AssignComponentId(instance),
|
||||
instance);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invokes the handler corresponding to an event.
|
||||
/// </summary>
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ namespace Microsoft.Blazor.Rendering
|
|||
/// </summary>
|
||||
/// <param name="component">The <see cref="IComponent"/>.</param>
|
||||
/// <returns>The assigned identifier for the <see cref="IComponent"/>.</returns>
|
||||
protected internal int AssignComponentId(IComponent component)
|
||||
protected int AssignComponentId(IComponent component)
|
||||
{
|
||||
lock (_componentStateById)
|
||||
{
|
||||
|
|
@ -42,6 +42,23 @@ namespace Microsoft.Blazor.Rendering
|
|||
}
|
||||
}
|
||||
|
||||
internal void InstantiateChildComponent(ref RenderTreeNode node)
|
||||
{
|
||||
if (node.NodeType != RenderTreeNodeType.Component)
|
||||
{
|
||||
throw new ArgumentException($"The node's {nameof(RenderTreeNode.NodeType)} property must equal {RenderTreeNodeType.Component}", nameof(node));
|
||||
}
|
||||
|
||||
if (node.Component != null)
|
||||
{
|
||||
throw new ArgumentException($"The node already has a non-null component instance", nameof(node));
|
||||
}
|
||||
|
||||
var newComponent = (IComponent)Activator.CreateInstance(node.ComponentType);
|
||||
var newComponentId = AssignComponentId(newComponent);
|
||||
node.SetChildComponentInstance(newComponentId, newComponent);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the visible UI to display the supplied <paramref name="renderTree"/>
|
||||
/// at the location corresponding to the <paramref name="componentId"/>.
|
||||
|
|
|
|||
|
|
@ -18,14 +18,15 @@ namespace Microsoft.Blazor.Test
|
|||
public void RecognizesEquivalentNodesAsSame(Action<RenderTreeBuilder> appendAction)
|
||||
{
|
||||
// Arrange
|
||||
var oldTree = new RenderTreeBuilder(new FakeRenderer());
|
||||
var newTree = new RenderTreeBuilder(new FakeRenderer());
|
||||
var diff = new RenderTreeDiffComputer();
|
||||
var renderer = new FakeRenderer();
|
||||
var oldTree = new RenderTreeBuilder(renderer);
|
||||
var newTree = new RenderTreeBuilder(renderer);
|
||||
var diff = new RenderTreeDiffComputer(renderer);
|
||||
appendAction(oldTree);
|
||||
appendAction(newTree);
|
||||
|
||||
// Act
|
||||
var result = diff.ComputeDifference(oldTree.GetNodes(), newTree.GetNodes());
|
||||
var result = diff.ApplyNewRenderTreeVersion(oldTree.GetNodes(), newTree.GetNodes());
|
||||
|
||||
// Assert
|
||||
Assert.Empty(result.Edits);
|
||||
|
|
@ -49,9 +50,10 @@ namespace Microsoft.Blazor.Test
|
|||
public void RecognizesNewItemsBeingInserted()
|
||||
{
|
||||
// Arrange
|
||||
var oldTree = new RenderTreeBuilder(new FakeRenderer());
|
||||
var newTree = new RenderTreeBuilder(new FakeRenderer());
|
||||
var diff = new RenderTreeDiffComputer();
|
||||
var renderer = new FakeRenderer();
|
||||
var oldTree = new RenderTreeBuilder(renderer);
|
||||
var newTree = new RenderTreeBuilder(renderer);
|
||||
var diff = new RenderTreeDiffComputer(renderer);
|
||||
oldTree.AddText(0, "text0");
|
||||
oldTree.AddText(2, "text2");
|
||||
newTree.AddText(0, "text0");
|
||||
|
|
@ -59,7 +61,7 @@ namespace Microsoft.Blazor.Test
|
|||
newTree.AddText(2, "text2");
|
||||
|
||||
// Act
|
||||
var result = diff.ComputeDifference(oldTree.GetNodes(), newTree.GetNodes());
|
||||
var result = diff.ApplyNewRenderTreeVersion(oldTree.GetNodes(), newTree.GetNodes());
|
||||
|
||||
// Assert
|
||||
Assert.Collection(result.Edits,
|
||||
|
|
@ -74,9 +76,10 @@ namespace Microsoft.Blazor.Test
|
|||
public void RecognizesOldItemsBeingRemoved()
|
||||
{
|
||||
// Arrange
|
||||
var oldTree = new RenderTreeBuilder(new FakeRenderer());
|
||||
var newTree = new RenderTreeBuilder(new FakeRenderer());
|
||||
var diff = new RenderTreeDiffComputer();
|
||||
var renderer = new FakeRenderer();
|
||||
var oldTree = new RenderTreeBuilder(renderer);
|
||||
var newTree = new RenderTreeBuilder(renderer);
|
||||
var diff = new RenderTreeDiffComputer(renderer);
|
||||
oldTree.AddText(0, "text0");
|
||||
oldTree.AddText(1, "text1");
|
||||
oldTree.AddText(2, "text2");
|
||||
|
|
@ -84,7 +87,7 @@ namespace Microsoft.Blazor.Test
|
|||
newTree.AddText(2, "text2");
|
||||
|
||||
// Act
|
||||
var result = diff.ComputeDifference(oldTree.GetNodes(), newTree.GetNodes());
|
||||
var result = diff.ApplyNewRenderTreeVersion(oldTree.GetNodes(), newTree.GetNodes());
|
||||
|
||||
// Assert
|
||||
Assert.Collection(result.Edits,
|
||||
|
|
@ -95,9 +98,10 @@ namespace Microsoft.Blazor.Test
|
|||
public void RecognizesTrailingSequenceWithinLoopBlockBeingRemoved()
|
||||
{
|
||||
// Arrange
|
||||
var oldTree = new RenderTreeBuilder(new FakeRenderer());
|
||||
var newTree = new RenderTreeBuilder(new FakeRenderer());
|
||||
var diff = new RenderTreeDiffComputer();
|
||||
var renderer = new FakeRenderer();
|
||||
var oldTree = new RenderTreeBuilder(renderer);
|
||||
var newTree = new RenderTreeBuilder(renderer);
|
||||
var diff = new RenderTreeDiffComputer(renderer);
|
||||
oldTree.AddText(0, "x"); // Loop start
|
||||
oldTree.AddText(1, "x"); // Will be removed
|
||||
oldTree.AddText(2, "x"); // Will be removed
|
||||
|
|
@ -106,7 +110,7 @@ namespace Microsoft.Blazor.Test
|
|||
newTree.AddText(0, "x"); // Loop start
|
||||
|
||||
// Act
|
||||
var result = diff.ComputeDifference(oldTree.GetNodes(), newTree.GetNodes());
|
||||
var result = diff.ApplyNewRenderTreeVersion(oldTree.GetNodes(), newTree.GetNodes());
|
||||
|
||||
// Assert
|
||||
Assert.Collection(result.Edits,
|
||||
|
|
@ -118,9 +122,10 @@ namespace Microsoft.Blazor.Test
|
|||
public void RecognizesTrailingSequenceWithinLoopBlockBeingAppended()
|
||||
{
|
||||
// Arrange
|
||||
var oldTree = new RenderTreeBuilder(new FakeRenderer());
|
||||
var newTree = new RenderTreeBuilder(new FakeRenderer());
|
||||
var diff = new RenderTreeDiffComputer();
|
||||
var renderer = new FakeRenderer();
|
||||
var oldTree = new RenderTreeBuilder(renderer);
|
||||
var newTree = new RenderTreeBuilder(renderer);
|
||||
var diff = new RenderTreeDiffComputer(renderer);
|
||||
oldTree.AddText(0, "x"); // Loop start
|
||||
oldTree.AddText(0, "x"); // Loop start
|
||||
newTree.AddText(0, "x"); // Loop start
|
||||
|
|
@ -129,7 +134,7 @@ namespace Microsoft.Blazor.Test
|
|||
newTree.AddText(0, "x"); // Loop start
|
||||
|
||||
// Act
|
||||
var result = diff.ComputeDifference(oldTree.GetNodes(), newTree.GetNodes());
|
||||
var result = diff.ApplyNewRenderTreeVersion(oldTree.GetNodes(), newTree.GetNodes());
|
||||
|
||||
// Assert
|
||||
Assert.Collection(result.Edits,
|
||||
|
|
@ -149,9 +154,10 @@ namespace Microsoft.Blazor.Test
|
|||
public void RecognizesTrailingLoopBlockBeingRemoved()
|
||||
{
|
||||
// Arrange
|
||||
var oldTree = new RenderTreeBuilder(new FakeRenderer());
|
||||
var newTree = new RenderTreeBuilder(new FakeRenderer());
|
||||
var diff = new RenderTreeDiffComputer();
|
||||
var renderer = new FakeRenderer();
|
||||
var oldTree = new RenderTreeBuilder(renderer);
|
||||
var newTree = new RenderTreeBuilder(renderer);
|
||||
var diff = new RenderTreeDiffComputer(renderer);
|
||||
oldTree.AddText(0, "x");
|
||||
oldTree.AddText(1, "x");
|
||||
oldTree.AddText(0, "x"); // Will be removed
|
||||
|
|
@ -160,7 +166,7 @@ namespace Microsoft.Blazor.Test
|
|||
newTree.AddText(1, "x");
|
||||
|
||||
// Act
|
||||
var result = diff.ComputeDifference(oldTree.GetNodes(), newTree.GetNodes());
|
||||
var result = diff.ApplyNewRenderTreeVersion(oldTree.GetNodes(), newTree.GetNodes());
|
||||
|
||||
// Assert
|
||||
Assert.Collection(result.Edits,
|
||||
|
|
@ -172,9 +178,10 @@ namespace Microsoft.Blazor.Test
|
|||
public void RecognizesTrailingLoopBlockBeingAdded()
|
||||
{
|
||||
// Arrange
|
||||
var oldTree = new RenderTreeBuilder(new FakeRenderer());
|
||||
var newTree = new RenderTreeBuilder(new FakeRenderer());
|
||||
var diff = new RenderTreeDiffComputer();
|
||||
var renderer = new FakeRenderer();
|
||||
var oldTree = new RenderTreeBuilder(renderer);
|
||||
var newTree = new RenderTreeBuilder(renderer);
|
||||
var diff = new RenderTreeDiffComputer(renderer);
|
||||
oldTree.AddText(0, "x");
|
||||
oldTree.AddText(1, "x");
|
||||
newTree.AddText(0, "x");
|
||||
|
|
@ -183,7 +190,7 @@ namespace Microsoft.Blazor.Test
|
|||
newTree.AddText(1, "x"); // Will be added
|
||||
|
||||
// Act
|
||||
var result = diff.ComputeDifference(oldTree.GetNodes(), newTree.GetNodes());
|
||||
var result = diff.ApplyNewRenderTreeVersion(oldTree.GetNodes(), newTree.GetNodes());
|
||||
|
||||
// Assert
|
||||
Assert.Collection(result.Edits,
|
||||
|
|
@ -203,9 +210,10 @@ namespace Microsoft.Blazor.Test
|
|||
public void RecognizesLeadingLoopBlockItemsBeingAdded()
|
||||
{
|
||||
// Arrange
|
||||
var oldTree = new RenderTreeBuilder(new FakeRenderer());
|
||||
var newTree = new RenderTreeBuilder(new FakeRenderer());
|
||||
var diff = new RenderTreeDiffComputer();
|
||||
var renderer = new FakeRenderer();
|
||||
var oldTree = new RenderTreeBuilder(renderer);
|
||||
var newTree = new RenderTreeBuilder(renderer);
|
||||
var diff = new RenderTreeDiffComputer(renderer);
|
||||
oldTree.AddText(2, "x");
|
||||
oldTree.AddText(2, "x"); // Note that the '0' and '1' items are not present on this iteration
|
||||
newTree.AddText(2, "x");
|
||||
|
|
@ -214,7 +222,7 @@ namespace Microsoft.Blazor.Test
|
|||
newTree.AddText(2, "x");
|
||||
|
||||
// Act
|
||||
var result = diff.ComputeDifference(oldTree.GetNodes(), newTree.GetNodes());
|
||||
var result = diff.ApplyNewRenderTreeVersion(oldTree.GetNodes(), newTree.GetNodes());
|
||||
|
||||
// Assert
|
||||
Assert.Collection(result.Edits,
|
||||
|
|
@ -234,9 +242,10 @@ namespace Microsoft.Blazor.Test
|
|||
public void RecognizesLeadingLoopBlockItemsBeingRemoved()
|
||||
{
|
||||
// Arrange
|
||||
var oldTree = new RenderTreeBuilder(new FakeRenderer());
|
||||
var newTree = new RenderTreeBuilder(new FakeRenderer());
|
||||
var diff = new RenderTreeDiffComputer();
|
||||
var renderer = new FakeRenderer();
|
||||
var oldTree = new RenderTreeBuilder(renderer);
|
||||
var newTree = new RenderTreeBuilder(renderer);
|
||||
var diff = new RenderTreeDiffComputer(renderer);
|
||||
oldTree.AddText(2, "x");
|
||||
oldTree.AddText(0, "x");
|
||||
oldTree.AddText(1, "x");
|
||||
|
|
@ -245,7 +254,7 @@ namespace Microsoft.Blazor.Test
|
|||
newTree.AddText(2, "x"); // Note that the '0' and '1' items are not present on this iteration
|
||||
|
||||
// Act
|
||||
var result = diff.ComputeDifference(oldTree.GetNodes(), newTree.GetNodes());
|
||||
var result = diff.ApplyNewRenderTreeVersion(oldTree.GetNodes(), newTree.GetNodes());
|
||||
|
||||
// Assert
|
||||
Assert.Collection(result.Edits,
|
||||
|
|
@ -257,14 +266,15 @@ namespace Microsoft.Blazor.Test
|
|||
public void HandlesAdjacentItemsBeingRemovedAndInsertedAtOnce()
|
||||
{
|
||||
// Arrange
|
||||
var oldTree = new RenderTreeBuilder(new FakeRenderer());
|
||||
var newTree = new RenderTreeBuilder(new FakeRenderer());
|
||||
var diff = new RenderTreeDiffComputer();
|
||||
var renderer = new FakeRenderer();
|
||||
var oldTree = new RenderTreeBuilder(renderer);
|
||||
var newTree = new RenderTreeBuilder(renderer);
|
||||
var diff = new RenderTreeDiffComputer(renderer);
|
||||
oldTree.AddText(0, "text");
|
||||
newTree.AddText(1, "text");
|
||||
|
||||
// Act
|
||||
var result = diff.ComputeDifference(oldTree.GetNodes(), newTree.GetNodes());
|
||||
var result = diff.ApplyNewRenderTreeVersion(oldTree.GetNodes(), newTree.GetNodes());
|
||||
|
||||
// Assert
|
||||
Assert.Collection(result.Edits,
|
||||
|
|
@ -276,16 +286,17 @@ namespace Microsoft.Blazor.Test
|
|||
public void RecognizesTextUpdates()
|
||||
{
|
||||
// Arrange
|
||||
var oldTree = new RenderTreeBuilder(new FakeRenderer());
|
||||
var newTree = new RenderTreeBuilder(new FakeRenderer());
|
||||
var diff = new RenderTreeDiffComputer();
|
||||
var renderer = new FakeRenderer();
|
||||
var oldTree = new RenderTreeBuilder(renderer);
|
||||
var newTree = new RenderTreeBuilder(renderer);
|
||||
var diff = new RenderTreeDiffComputer(renderer);
|
||||
oldTree.AddText(123, "old text 1");
|
||||
oldTree.AddText(182, "old text 2");
|
||||
newTree.AddText(123, "new text 1");
|
||||
newTree.AddText(182, "new text 2");
|
||||
|
||||
// Act
|
||||
var result = diff.ComputeDifference(oldTree.GetNodes(), newTree.GetNodes());
|
||||
var result = diff.ApplyNewRenderTreeVersion(oldTree.GetNodes(), newTree.GetNodes());
|
||||
|
||||
// Assert
|
||||
Assert.Collection(result.Edits,
|
||||
|
|
@ -309,16 +320,17 @@ namespace Microsoft.Blazor.Test
|
|||
// decide just to throw in this scenario, since it's unnecessary to support it.
|
||||
|
||||
// Arrange
|
||||
var oldTree = new RenderTreeBuilder(new FakeRenderer());
|
||||
var newTree = new RenderTreeBuilder(new FakeRenderer());
|
||||
var diff = new RenderTreeDiffComputer();
|
||||
var renderer = new FakeRenderer();
|
||||
var oldTree = new RenderTreeBuilder(renderer);
|
||||
var newTree = new RenderTreeBuilder(renderer);
|
||||
var diff = new RenderTreeDiffComputer(renderer);
|
||||
oldTree.OpenElement(123, "old element");
|
||||
oldTree.CloseElement();
|
||||
newTree.OpenElement(123, "new element");
|
||||
newTree.CloseElement();
|
||||
|
||||
// Act
|
||||
var result = diff.ComputeDifference(oldTree.GetNodes(), newTree.GetNodes());
|
||||
var result = diff.ApplyNewRenderTreeVersion(oldTree.GetNodes(), newTree.GetNodes());
|
||||
|
||||
// Assert
|
||||
Assert.Collection(result.Edits,
|
||||
|
|
@ -334,14 +346,15 @@ namespace Microsoft.Blazor.Test
|
|||
public void RecognizesComponentTypeChangesAtSameSequenceNumber()
|
||||
{
|
||||
// Arrange
|
||||
var oldTree = new RenderTreeBuilder(new FakeRenderer());
|
||||
var newTree = new RenderTreeBuilder(new FakeRenderer());
|
||||
var diff = new RenderTreeDiffComputer();
|
||||
var renderer = new FakeRenderer();
|
||||
var oldTree = new RenderTreeBuilder(renderer);
|
||||
var newTree = new RenderTreeBuilder(renderer);
|
||||
var diff = new RenderTreeDiffComputer(renderer);
|
||||
oldTree.AddComponentElement<FakeComponent>(123);
|
||||
newTree.AddComponentElement<FakeComponent2>(123);
|
||||
|
||||
// Act
|
||||
var result = diff.ComputeDifference(oldTree.GetNodes(), newTree.GetNodes());
|
||||
var result = diff.ApplyNewRenderTreeVersion(oldTree.GetNodes(), newTree.GetNodes());
|
||||
|
||||
// Assert
|
||||
Assert.Collection(result.Edits,
|
||||
|
|
@ -357,9 +370,10 @@ namespace Microsoft.Blazor.Test
|
|||
public void RecognizesAttributesAdded()
|
||||
{
|
||||
// Arrange
|
||||
var oldTree = new RenderTreeBuilder(new FakeRenderer());
|
||||
var newTree = new RenderTreeBuilder(new FakeRenderer());
|
||||
var diff = new RenderTreeDiffComputer();
|
||||
var renderer = new FakeRenderer();
|
||||
var oldTree = new RenderTreeBuilder(renderer);
|
||||
var newTree = new RenderTreeBuilder(renderer);
|
||||
var diff = new RenderTreeDiffComputer(renderer);
|
||||
oldTree.OpenElement(0, "My element");
|
||||
oldTree.AddAttribute(1, "existing", "existing value");
|
||||
oldTree.CloseElement();
|
||||
|
|
@ -369,7 +383,7 @@ namespace Microsoft.Blazor.Test
|
|||
newTree.CloseElement();
|
||||
|
||||
// Act
|
||||
var result = diff.ComputeDifference(oldTree.GetNodes(), newTree.GetNodes());
|
||||
var result = diff.ApplyNewRenderTreeVersion(oldTree.GetNodes(), newTree.GetNodes());
|
||||
|
||||
// Assert
|
||||
Assert.Collection(result.Edits,
|
||||
|
|
@ -384,9 +398,10 @@ namespace Microsoft.Blazor.Test
|
|||
public void RecognizesAttributesRemoved()
|
||||
{
|
||||
// Arrange
|
||||
var oldTree = new RenderTreeBuilder(new FakeRenderer());
|
||||
var newTree = new RenderTreeBuilder(new FakeRenderer());
|
||||
var diff = new RenderTreeDiffComputer();
|
||||
var renderer = new FakeRenderer();
|
||||
var oldTree = new RenderTreeBuilder(renderer);
|
||||
var newTree = new RenderTreeBuilder(renderer);
|
||||
var diff = new RenderTreeDiffComputer(renderer);
|
||||
oldTree.OpenElement(0, "My element");
|
||||
oldTree.AddAttribute(1, "will be removed", "will be removed value");
|
||||
oldTree.AddAttribute(2, "will survive", "surviving value");
|
||||
|
|
@ -396,7 +411,7 @@ namespace Microsoft.Blazor.Test
|
|||
newTree.CloseElement();
|
||||
|
||||
// Act
|
||||
var result = diff.ComputeDifference(oldTree.GetNodes(), newTree.GetNodes());
|
||||
var result = diff.ApplyNewRenderTreeVersion(oldTree.GetNodes(), newTree.GetNodes());
|
||||
|
||||
// Assert
|
||||
Assert.Collection(result.Edits,
|
||||
|
|
@ -411,9 +426,10 @@ namespace Microsoft.Blazor.Test
|
|||
public void RecognizesAttributeStringValuesChanged()
|
||||
{
|
||||
// Arrange
|
||||
var oldTree = new RenderTreeBuilder(new FakeRenderer());
|
||||
var newTree = new RenderTreeBuilder(new FakeRenderer());
|
||||
var diff = new RenderTreeDiffComputer();
|
||||
var renderer = new FakeRenderer();
|
||||
var oldTree = new RenderTreeBuilder(renderer);
|
||||
var newTree = new RenderTreeBuilder(renderer);
|
||||
var diff = new RenderTreeDiffComputer(renderer);
|
||||
oldTree.OpenElement(0, "My element");
|
||||
oldTree.AddAttribute(1, "will remain", "will remain value");
|
||||
oldTree.AddAttribute(2, "will change", "will change value");
|
||||
|
|
@ -424,7 +440,7 @@ namespace Microsoft.Blazor.Test
|
|||
newTree.CloseElement();
|
||||
|
||||
// Act
|
||||
var result = diff.ComputeDifference(oldTree.GetNodes(), newTree.GetNodes());
|
||||
var result = diff.ApplyNewRenderTreeVersion(oldTree.GetNodes(), newTree.GetNodes());
|
||||
|
||||
// Assert
|
||||
Assert.Collection(result.Edits,
|
||||
|
|
@ -439,9 +455,10 @@ namespace Microsoft.Blazor.Test
|
|||
public void RecognizesAttributeEventHandlerValuesChanged()
|
||||
{
|
||||
// Arrange
|
||||
var oldTree = new RenderTreeBuilder(new FakeRenderer());
|
||||
var newTree = new RenderTreeBuilder(new FakeRenderer());
|
||||
var diff = new RenderTreeDiffComputer();
|
||||
var renderer = new FakeRenderer();
|
||||
var oldTree = new RenderTreeBuilder(renderer);
|
||||
var newTree = new RenderTreeBuilder(renderer);
|
||||
var diff = new RenderTreeDiffComputer(renderer);
|
||||
UIEventHandler retainedHandler = _ => { };
|
||||
UIEventHandler removedHandler = _ => { };
|
||||
UIEventHandler addedHandler = _ => { };
|
||||
|
|
@ -455,7 +472,7 @@ namespace Microsoft.Blazor.Test
|
|||
newTree.CloseElement();
|
||||
|
||||
// Act
|
||||
var result = diff.ComputeDifference(oldTree.GetNodes(), newTree.GetNodes());
|
||||
var result = diff.ApplyNewRenderTreeVersion(oldTree.GetNodes(), newTree.GetNodes());
|
||||
|
||||
// Assert
|
||||
Assert.Collection(result.Edits,
|
||||
|
|
@ -470,9 +487,10 @@ namespace Microsoft.Blazor.Test
|
|||
public void RecognizesAttributeNamesChangedAtSameSourceSequence()
|
||||
{
|
||||
// Arrange
|
||||
var oldTree = new RenderTreeBuilder(new FakeRenderer());
|
||||
var newTree = new RenderTreeBuilder(new FakeRenderer());
|
||||
var diff = new RenderTreeDiffComputer();
|
||||
var renderer = new FakeRenderer();
|
||||
var oldTree = new RenderTreeBuilder(renderer);
|
||||
var newTree = new RenderTreeBuilder(renderer);
|
||||
var diff = new RenderTreeDiffComputer(renderer);
|
||||
oldTree.OpenElement(0, "My element");
|
||||
oldTree.AddAttribute(1, "oldname", "same value");
|
||||
oldTree.CloseElement();
|
||||
|
|
@ -481,7 +499,7 @@ namespace Microsoft.Blazor.Test
|
|||
newTree.CloseElement();
|
||||
|
||||
// Act
|
||||
var result = diff.ComputeDifference(oldTree.GetNodes(), newTree.GetNodes());
|
||||
var result = diff.ApplyNewRenderTreeVersion(oldTree.GetNodes(), newTree.GetNodes());
|
||||
|
||||
// Assert
|
||||
Assert.Collection(result.Edits,
|
||||
|
|
@ -501,9 +519,10 @@ namespace Microsoft.Blazor.Test
|
|||
public void DiffsElementsHierarchically()
|
||||
{
|
||||
// Arrange
|
||||
var oldTree = new RenderTreeBuilder(new FakeRenderer());
|
||||
var newTree = new RenderTreeBuilder(new FakeRenderer());
|
||||
var diff = new RenderTreeDiffComputer();
|
||||
var renderer = new FakeRenderer();
|
||||
var oldTree = new RenderTreeBuilder(renderer);
|
||||
var newTree = new RenderTreeBuilder(renderer);
|
||||
var diff = new RenderTreeDiffComputer(renderer);
|
||||
oldTree.AddText(09, "unrelated");
|
||||
oldTree.OpenElement(10, "root");
|
||||
oldTree.OpenElement(11, "child");
|
||||
|
|
@ -523,7 +542,7 @@ namespace Microsoft.Blazor.Test
|
|||
newTree.CloseElement();
|
||||
|
||||
// Act
|
||||
var result = diff.ComputeDifference(oldTree.GetNodes(), newTree.GetNodes());
|
||||
var result = diff.ApplyNewRenderTreeVersion(oldTree.GetNodes(), newTree.GetNodes());
|
||||
|
||||
// Assert
|
||||
Assert.Collection(result.Edits,
|
||||
|
|
@ -544,9 +563,10 @@ namespace Microsoft.Blazor.Test
|
|||
public void SkipsUnmodifiedSubtrees()
|
||||
{
|
||||
// Arrange
|
||||
var oldTree = new RenderTreeBuilder(new FakeRenderer());
|
||||
var newTree = new RenderTreeBuilder(new FakeRenderer());
|
||||
var diff = new RenderTreeDiffComputer();
|
||||
var renderer = new FakeRenderer();
|
||||
var oldTree = new RenderTreeBuilder(renderer);
|
||||
var newTree = new RenderTreeBuilder(renderer);
|
||||
var diff = new RenderTreeDiffComputer(renderer);
|
||||
oldTree.OpenElement(10, "root");
|
||||
oldTree.AddText(11, "Text that will change");
|
||||
oldTree.OpenElement(12, "Subtree that will not change");
|
||||
|
|
@ -566,7 +586,7 @@ namespace Microsoft.Blazor.Test
|
|||
newTree.CloseElement();
|
||||
|
||||
// Act
|
||||
var result = diff.ComputeDifference(oldTree.GetNodes(), newTree.GetNodes());
|
||||
var result = diff.ApplyNewRenderTreeVersion(oldTree.GetNodes(), newTree.GetNodes());
|
||||
|
||||
// Assert
|
||||
Assert.Collection(result.Edits,
|
||||
|
|
@ -583,9 +603,10 @@ namespace Microsoft.Blazor.Test
|
|||
public void SkipsUnmodifiedTrailingSiblings()
|
||||
{
|
||||
// Arrange
|
||||
var oldTree = new RenderTreeBuilder(new FakeRenderer());
|
||||
var newTree = new RenderTreeBuilder(new FakeRenderer());
|
||||
var diff = new RenderTreeDiffComputer();
|
||||
var renderer = new FakeRenderer();
|
||||
var oldTree = new RenderTreeBuilder(renderer);
|
||||
var newTree = new RenderTreeBuilder(renderer);
|
||||
var diff = new RenderTreeDiffComputer(renderer);
|
||||
oldTree.AddText(10, "text1");
|
||||
oldTree.AddText(11, "text2");
|
||||
oldTree.AddText(12, "text3");
|
||||
|
|
@ -596,7 +617,7 @@ namespace Microsoft.Blazor.Test
|
|||
newTree.AddText(13, "text4");
|
||||
|
||||
// Act
|
||||
var result = diff.ComputeDifference(oldTree.GetNodes(), newTree.GetNodes());
|
||||
var result = diff.ApplyNewRenderTreeVersion(oldTree.GetNodes(), newTree.GetNodes());
|
||||
|
||||
// Assert
|
||||
Assert.Collection(result.Edits,
|
||||
|
|
@ -607,6 +628,92 @@ namespace Microsoft.Blazor.Test
|
|||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void InstantiatesChildComponentsForInsertedNodes()
|
||||
{
|
||||
// Arrange
|
||||
var renderer = new FakeRenderer();
|
||||
var oldTree = new RenderTreeBuilder(renderer);
|
||||
var newTree = new RenderTreeBuilder(renderer);
|
||||
var diff = new RenderTreeDiffComputer(renderer);
|
||||
oldTree.AddText(10, "text1"); // 0: text1
|
||||
oldTree.OpenElement(11, "container"); // 1: <container>
|
||||
oldTree.CloseElement(); // </container>
|
||||
newTree.AddText(10, "text1"); // 0: text1
|
||||
newTree.OpenElement(11, "container"); // 1: <container>
|
||||
newTree.AddComponentElement<FakeComponent>(12); // 2: <FakeComponent>
|
||||
newTree.CloseElement(); // </FakeComponent>
|
||||
newTree.AddComponentElement<FakeComponent2>(13); // 3: <FakeComponent2>
|
||||
newTree.CloseElement(); // </FakeComponent2>
|
||||
newTree.CloseElement(); // </container>
|
||||
|
||||
// Act
|
||||
var result = diff.ApplyNewRenderTreeVersion(oldTree.GetNodes(), newTree.GetNodes());
|
||||
|
||||
// Assert
|
||||
Assert.Collection(result.Edits,
|
||||
entry => AssertEdit(entry, RenderTreeEditType.StepIn, 1),
|
||||
entry =>
|
||||
{
|
||||
AssertEdit(entry, RenderTreeEditType.PrependNode, 0);
|
||||
Assert.Equal(2, entry.NewTreeIndex);
|
||||
|
||||
var newTreeNode = newTree.GetNodes().Array[entry.NewTreeIndex];
|
||||
Assert.Equal(0, newTreeNode.ComponentId);
|
||||
Assert.IsType<FakeComponent>(newTreeNode.Component);
|
||||
},
|
||||
entry =>
|
||||
{
|
||||
AssertEdit(entry, RenderTreeEditType.PrependNode, 1);
|
||||
Assert.Equal(3, entry.NewTreeIndex);
|
||||
|
||||
var newTreeNode = newTree.GetNodes().Array[entry.NewTreeIndex];
|
||||
Assert.Equal(1, newTreeNode.ComponentId);
|
||||
Assert.IsType<FakeComponent2>(newTreeNode.Component);
|
||||
},
|
||||
entry => AssertEdit(entry, RenderTreeEditType.StepOut, 0));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RetainsChildComponentsForExistingNodes()
|
||||
{
|
||||
// Arrange
|
||||
var renderer = new FakeRenderer();
|
||||
var oldTree = new RenderTreeBuilder(renderer);
|
||||
var newTree = new RenderTreeBuilder(renderer);
|
||||
var diff = new RenderTreeDiffComputer(renderer);
|
||||
oldTree.AddText(10, "text1"); // 0: text1
|
||||
oldTree.OpenElement(11, "container"); // 1: <container>
|
||||
oldTree.AddComponentElement<FakeComponent>(12); // 2: <FakeComponent>
|
||||
oldTree.CloseElement(); // </FakeComponent>
|
||||
oldTree.AddComponentElement<FakeComponent2>(13); // 3: <FakeComponent2>
|
||||
oldTree.CloseElement(); // </FakeComponent2>
|
||||
oldTree.CloseElement(); // </container
|
||||
newTree.AddText(10, "text1"); // 0: text1
|
||||
newTree.OpenElement(11, "container"); // 1: <container>
|
||||
newTree.AddComponentElement<FakeComponent>(12); // 2: <FakeComponent>
|
||||
newTree.CloseElement(); // </FakeComponent>
|
||||
newTree.AddComponentElement<FakeComponent2>(13); // 3: <FakeComponent2>
|
||||
newTree.CloseElement(); // </FakeComponent2>
|
||||
newTree.CloseElement(); // </container
|
||||
|
||||
diff.ApplyNewRenderTreeVersion(new RenderTreeBuilder(renderer).GetNodes(), oldTree.GetNodes());
|
||||
var originalFakeComponentInstance = oldTree.GetNodes().Array[2].Component;
|
||||
var originalFakeComponent2Instance = oldTree.GetNodes().Array[3].Component;
|
||||
|
||||
// Act
|
||||
var result = diff.ApplyNewRenderTreeVersion(oldTree.GetNodes(), newTree.GetNodes());
|
||||
var newNode1 = newTree.GetNodes().Array[2];
|
||||
var newNode2 = newTree.GetNodes().Array[3];
|
||||
|
||||
// Assert
|
||||
Assert.Empty(result.Edits);
|
||||
Assert.Equal(0, newNode1.ComponentId);
|
||||
Assert.Equal(1, newNode2.ComponentId);
|
||||
Assert.Same(originalFakeComponentInstance, newNode1.Component);
|
||||
Assert.Same(originalFakeComponent2Instance, newNode2.Component);
|
||||
}
|
||||
|
||||
private class FakeRenderer : Renderer
|
||||
{
|
||||
internal protected override void UpdateDisplay(int componentId, RenderTreeDiff renderTreeDiff)
|
||||
|
|
|
|||
|
|
@ -45,6 +45,7 @@ namespace Microsoft.Blazor.Test
|
|||
{
|
||||
builder.AddText(0, "Hello");
|
||||
builder.AddComponentElement<MessageComponent>(1);
|
||||
builder.CloseElement();
|
||||
});
|
||||
|
||||
// Act/Assert
|
||||
|
|
@ -95,6 +96,7 @@ namespace Microsoft.Blazor.Test
|
|||
var parentComponent = new TestComponent(builder =>
|
||||
{
|
||||
builder.AddComponentElement<MessageComponent>(0);
|
||||
builder.CloseElement();
|
||||
});
|
||||
var parentComponentId = renderer.AssignComponentId(parentComponent);
|
||||
renderer.RenderComponent(parentComponentId);
|
||||
|
|
@ -153,6 +155,7 @@ namespace Microsoft.Blazor.Test
|
|||
var parentComponent = new TestComponent(builder =>
|
||||
{
|
||||
builder.AddComponentElement<EventComponent>(0);
|
||||
builder.CloseElement();
|
||||
});
|
||||
var parentComponentId = renderer.AssignComponentId(parentComponent);
|
||||
renderer.RenderComponent(parentComponentId);
|
||||
|
|
@ -298,6 +301,36 @@ namespace Microsoft.Blazor.Test
|
|||
Assert.True(didRender);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void PreservesChildComponentInstancesWithNoAttributes()
|
||||
{
|
||||
// Arrange: First render, capturing child component instance
|
||||
var renderer = new TestRenderer();
|
||||
var message = "Hello";
|
||||
var component = new TestComponent(builder =>
|
||||
{
|
||||
builder.AddText(0, message);
|
||||
builder.AddComponentElement<MessageComponent>(1);
|
||||
builder.CloseElement();
|
||||
});
|
||||
|
||||
var rootComponentId = renderer.AssignComponentId(component);
|
||||
renderer.RenderComponent(rootComponentId);
|
||||
|
||||
var nestedComponentInstance = (MessageComponent)renderer.RenderTreesByComponentId[rootComponentId]
|
||||
.Single(node => node.NodeType == RenderTreeNodeType.Component)
|
||||
.Component;
|
||||
|
||||
// Act: Second render
|
||||
message = "Modified message";
|
||||
renderer.RenderComponent(rootComponentId);
|
||||
|
||||
// Assert
|
||||
Assert.Collection(renderer.RenderTreesByComponentId[rootComponentId],
|
||||
node => AssertNode.Text(node, "Modified message"),
|
||||
node => Assert.Same(nestedComponentInstance, node.Component));
|
||||
}
|
||||
|
||||
private class NoOpRenderer : Renderer
|
||||
{
|
||||
public new int AssignComponentId(IComponent component)
|
||||
|
|
|
|||
Loading…
Reference in New Issue