Liero
Liero

Reputation: 27338

Blazor - SetParameters - why string parameter binding behaves differently from a complex type

Consider child component with string parameter and with a complex type parameter.

<Child Name="@strProperty" />
<Child Model="@compelexType" />

Assuming neither parameter has changed, calling StateHasChanged on the parent component causes the Child component with complex parameter to rerender, but not with string parameter.

See this demo: https://blazorrepl.com/repl/wbYguGuA515uMYR742

How do you explain different behavior?

Upvotes: 1

Views: 1103

Answers (3)

MrC aka Shaun Curtis
MrC aka Shaun Curtis

Reputation: 30016

This isn't an answer per se, I just need more space than in a comment.

I don't believe the quoted documentation fully explains the observed results.

The document states:

By default, Razor components inherit from the ComponentBase base class, which contains logic to trigger rerendering at the following times:

  1. After applying an updated set of parameters from a parent component.
  2. After applying an updated value for a cascading parameter.
  3. After notification of an event and invoking one of its own event handlers.
  4. After a call to its own StateHasChanged method (see ASP.NET Core Razor component lifecycle).

These are all internal actions. The first two occur after SetParametersAsync has been called on the component. The last two from internal actions within the component.

Any child component activity, such as re-rendering, is triggered by the Renderer calling SetParametersAsync, not directly by the parent. The Renderer makes this decision based on detected changes to any child component Parameter's. So the reason the re-render occurs is because the Renderer is deciding that it's an object and not primitive type, nothing to do with ComponentBase.

To prove this I wrote a very basic component based on IComponent rather than ComponentBase. It looks like this:

using Blazor.Starter.Data;
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Rendering;
using System.Threading.Tasks;

namespace Blazor.Starter.Components.TestComponents
{
    public class ObjectTest : IComponent
    {
        [Parameter] public DataModel Model { get; set; }

        [Parameter] public int Value { get; set; }

        private RenderHandle _renderHandle;
        private int _renders;

        public void Attach(RenderHandle renderHandle)
        {
            _renderHandle = renderHandle;
        }

        public Task SetParametersAsync(ParameterView parameters)
        {
            parameters.SetParameterProperties(this);
            _renders++;
            this.Render();
            return Task.CompletedTask;
        }

        public void Render()
            => _renderHandle.Render(RenderComponent);

        private void RenderComponent(RenderTreeBuilder builder)
        {
            builder.OpenElement(0, "div");
            builder.AddContent(1, $"Rendered {_renders}");
            builder.CloseElement();
        }

    }
}

If you set Model in the parent SetParametersAsync is called, if you only set Value it doesn't. Same result.

Upvotes: 2

GSerg
GSerg

Reputation: 78155

Documentation:

Components inherited from ComponentBase skip rerenders due to parameter updates if either of the following are true:

  • All of the parameter values are of known immutable primitive types, such as int, string, DateTime, and haven't changed since the previous set of parameters were set.
  • The component's ShouldRender method returns false.

Upvotes: 1

Cornelis
Cornelis

Reputation: 1107

As @GSerg said in a comment: It's because the primitive property didn't change, so it does not update.

Upvotes: 0

Related Questions