Home > Mobile >  Proper use of OnAfterRender?
Proper use of OnAfterRender?

Time:01-23

I have a project in which I frequently use OnAfterRender() to call methods on child components. As a simple example, suppose I have two Blazor components, Counter.razor and CounterParent.razor:

Counter:


@page "/counter"

<p role="status">Current count: @currentCount</p>

<button  @onclick="IncrementCount">Click me</button>

@code {
    private int currentCount = 0;

    // This method is private out of the box,
    // but let's change it to public for this example
    public void IncrementCount() 
    {
        currentCount  ;
        StateHasChanged();
    }
}

CounterParent:

@page "/counterParent"
<h3>Counter Parent</h3>

<Counter @ref="counterChild"></Counter>

@code {

    private Counter counterChild;
    private bool loaded;

    protected override void OnAfterRender(bool firstRender)
    {
        if (!loaded && counterChild != null)
        {
            counterChild.IncrementCount(); // Do some operation on the child component
            loaded = true;
        }  
    }
}

The parent component in this example causes the child component (the counter) to be incremented by 1.

However, I have recently been advised that this is not a good practice, and that

On OnAfterRender{async} usage, it should (in general) only be used to do JS interop stuff.

So if this is poor practice what is the best practice for calling methods on child components when a page loads? Why might someone choose to

actually either disable it on ComponentBase based components, , or don't implement it on [their] own components?

What I tried:

Code like the above works. It is definitely slow and the user often sees an ugly mess as things load. I probably won't fix this current project (works well enough for what I need it to), but if there's a better way I'd like to be better informed.

CodePudding user response:

It's better to use OnParametersSet instead of OnAfterRender.

OnAfterRender is called after each rendering of the component. At this stage, the task of loading the component, receiving information and displaying them is finished. One of its uses is the initialization of JavaScript components that require the DOM to work; Like displaying a Bootstrap modal.
Note: any changes made to the field values in these events are not applied to the UI; Because they are in the final stage of UI rendering.

OnParameterSet called once when the component is initially loaded and again whenever the child component receives a new parameter from the parent component.

CodePudding user response:

The core problem here is @ref="counterChild" , that reference will not yet be set in OnInitialized or (the first) OnParametersSet of the parent.

"it should only be used to do JS interop stuff" is too strict, it should be used for logic that needs the render to be completed. Making use of a component reference qualifies too.

I would use one of:

  • Put this action in OnInitialized() of the child component. (Assumes you always run the same action on a new child).

  • trigger it by a parameter on the Child component. That could a simple IsLoaded boolean. Take action in OnParametersSet() of the child.

  • Keep it in OnAfterRender() but use firstRender instead of loading.

That last one is exactly what you already have and it's not totally wrong. But it automatically means you get 2 renders, not the best U/X.

  • Related