From 374d19c1a4c7798624fd818f0639a1a86a156c54 Mon Sep 17 00:00:00 2001 From: Steve Sanderson Date: Thu, 18 Jan 2018 00:29:00 +0000 Subject: [PATCH] Move child component instantiation into a post-render phase to bring it closer to how it will work with rendertree diffing This is also needed to make it possible to have a helper to insert Component nodes from Razor without doing anything messy to pass the RenderTreeBuilder into that heper. --- .../src/Rendering/RenderTreeNode.ts | 6 +++--- .../RenderTree/RenderTreeBuilder.cs | 11 +---------- .../RenderTree/RenderTreeNode.cs | 18 +++++++++++++++--- .../Rendering/ComponentState.cs | 18 ++++++++++++++++++ test/shared/AssertNode.cs | 8 +------- 5 files changed, 38 insertions(+), 23 deletions(-) diff --git a/src/Microsoft.Blazor.Browser.JS/src/Rendering/RenderTreeNode.ts b/src/Microsoft.Blazor.Browser.JS/src/Rendering/RenderTreeNode.ts index 9ef3d50dcd..36f227edac 100644 --- a/src/Microsoft.Blazor.Browser.JS/src/Rendering/RenderTreeNode.ts +++ b/src/Microsoft.Blazor.Browser.JS/src/Rendering/RenderTreeNode.ts @@ -1,6 +1,6 @@ import { System_String, System_Array, Pointer } from '../Platform/Platform'; import { platform } from '../Environment'; -const renderTreeNodeStructLength = 36; +const renderTreeNodeStructLength = 40; // To minimise GC pressure, instead of instantiating a JS object to represent each tree node, // we work in terms of pointers to the structs on the .NET heap, and use static functions that @@ -19,8 +19,8 @@ export const renderTreeNode = { attributeName: (node: RenderTreeNodePointer) => platform.readStringField(node, 16), attributeValue: (node: RenderTreeNodePointer) => platform.readStringField(node, 20), attributeEventHandlerValue: (node: RenderTreeNodePointer) => platform.readObjectField(node, 24), - componentId: (node: RenderTreeNodePointer) => platform.readInt32Field(node, 28), - component: (node: RenderTreeNodePointer) => platform.readObjectField(node, 32), + componentId: (node: RenderTreeNodePointer) => platform.readInt32Field(node, 32), + component: (node: RenderTreeNodePointer) => platform.readObjectField(node, 36), }; export enum NodeType { diff --git a/src/Microsoft.Blazor/RenderTree/RenderTreeBuilder.cs b/src/Microsoft.Blazor/RenderTree/RenderTreeBuilder.cs index e7b2494fbd..15f8a3098d 100644 --- a/src/Microsoft.Blazor/RenderTree/RenderTreeBuilder.cs +++ b/src/Microsoft.Blazor/RenderTree/RenderTreeBuilder.cs @@ -124,16 +124,7 @@ namespace Microsoft.Blazor.RenderTree /// /// The type of the child component. public void AddComponent() where TComponent: IComponent - { - // Later, instead of instantiating the child component here, we'll instead - // store a descriptor of the component (type, parameters) on the attributes - // of the appended nodes. Then after the tree is diffed against the - // previous tree, we'll either instantiate a new component or reuse the - // existing instance (and notify it about changes to parameters). - var instance = Activator.CreateInstance(); - var instanceId = _renderer.AssignComponentId(instance); - Append(RenderTreeNode.ChildComponent(instanceId, instance)); - } + => Append(RenderTreeNode.ChildComponent()); private void AssertCanAddAttribute() { diff --git a/src/Microsoft.Blazor/RenderTree/RenderTreeNode.cs b/src/Microsoft.Blazor/RenderTree/RenderTreeNode.cs index cb6f55f8bb..a91865af43 100644 --- a/src/Microsoft.Blazor/RenderTree/RenderTreeNode.cs +++ b/src/Microsoft.Blazor/RenderTree/RenderTreeNode.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using Microsoft.Blazor.Components; +using System; namespace Microsoft.Blazor.RenderTree { @@ -56,6 +57,12 @@ namespace Microsoft.Blazor.RenderTree /// public UIEventHandler AttributeEventHandlerValue { get; private set; } + /// + /// If the property equals , + /// gets the type of the child component. + /// + public Type ComponentType { get; private set; } + /// /// If the property equals , /// gets the child component instance identifier. @@ -94,16 +101,21 @@ namespace Microsoft.Blazor.RenderTree AttributeEventHandlerValue = value }; - internal static RenderTreeNode ChildComponent(int componentId, IComponent component) => new RenderTreeNode + internal static RenderTreeNode ChildComponent() where T: IComponent => new RenderTreeNode { NodeType = RenderTreeNodeType.Component, - ComponentId = componentId, - Component = component + ComponentType = typeof(T) }; internal void CloseElement(int descendantsEndIndex) { ElementDescendantsEndIndex = descendantsEndIndex; } + + internal void SetChildComponentInstance(int componentId, IComponent component) + { + ComponentId = componentId; + Component = component; + } } } diff --git a/src/Microsoft.Blazor/Rendering/ComponentState.cs b/src/Microsoft.Blazor/Rendering/ComponentState.cs index e5826d8180..ccbebab4da 100644 --- a/src/Microsoft.Blazor/Rendering/ComponentState.cs +++ b/src/Microsoft.Blazor/Rendering/ComponentState.cs @@ -43,9 +43,27 @@ namespace Microsoft.Blazor.Rendering _component.BuildRenderTree(_renderTreeBuilder); var renderTree = _renderTreeBuilder.GetNodes(); + EnsureChildComponentsInstantiated(renderTree); _renderer.UpdateDisplay(_componentId, renderTree); } + private void EnsureChildComponentsInstantiated(ArraySegment renderTree) + { + var array = renderTree.Array; + var offsetPlusCount = renderTree.Offset + renderTree.Count; + for (var i = renderTree.Offset; i < offsetPlusCount; 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); + } + } + } + /// /// Invokes the handler corresponding to an event. /// diff --git a/test/shared/AssertNode.cs b/test/shared/AssertNode.cs index cdd9d27735..683889d82e 100644 --- a/test/shared/AssertNode.cs +++ b/test/shared/AssertNode.cs @@ -44,13 +44,7 @@ namespace Microsoft.Blazor.Test.Shared public static void Component(RenderTreeNode node) where T : IComponent { Assert.Equal(RenderTreeNodeType.Component, node.NodeType); - - // Currently, we instantiate child components during the tree building phase. - // Later this will change so it happens during the tree diffing phase, so this - // logic will need to change. It will need to verify that we're tracking the - // information needed to instantiate the component. - Assert.NotNull(node.Component); - Assert.IsType(node.Component); + Assert.Equal(typeof(T), node.ComponentType); } } }