Search code examples
asp.net-coreblazor-server-side.net-8.0

How to prevent the business logic present inside the Blazor SSR component's OnInitialized method from executing twice?


I understand that a Blazor SSR component's OnInitialized method executes twice if pre-rendering is enabled. I have written the business logic, for e.g., loading all the products from the database, inside the OnInitialized method. How do I prevent the business logic from executing twice? Do I have to keep track of it using a local variable such as IsInitialized = false, and then set it to true once the business logic executes. Does Blazor have a built-in way for detecting this? Or should I move the business logic to a different lifecycle method?

[Update 9/23] Adding the code here for more clarity (implemented @Rena 's 4th answer). Removed all the extraneous code. All components derive from BaseComponent, in which I want to find out the signed-in user. Hence I call the method to get the usercontext in the OnInitialized method, but I want that logic to execute only once. Currently both GetUserContextFromAuthenticationState and GetProductsListFromDatabase are being executed twice. isInitialized is always false.

In the App.razor, I don't specify the render mode, so the application starts in Static Server Render mode. And in the BaseComponent, I specify the Interactive Server Render mode.

BaseComponent.razor

@rendermode InteractiveServer
@code {
    private bool isInitialized = false;
    protected override async Task OnInitializedAsync()
    {
        if (!isInitialized)
        {
            var user = GetUserContextFromAuthenticationState();
            isInitialized = true;
        }
    }
}

Products.razor

@inherits BaseComponent
@code {
    private bool isInitialized = false;
    protected override async Task OnInitializedAsync()
    {
        await base.OnInitializedAsync().ConfigureAwait(false);
        if (!isInitialized)
        {
            var products = GetProductsListFromDatabase();
            isInitialized = true;
        }
    }
}

[Update 9/24] Detecting pre-render using the IHttpContextAccessor.HttpContext works (@Rena answer 2). Marking that as answer.

Though I would like to know why the above code doesn't work. Are the blazor components instantiated twice? That could be the reason why isInitialized is always false. Should I ask this as a separate question? I have spent so much time trying to understand blazor component lifecycle, and still don't get it.


Solution

  • There are several ways we can achieve:

    1.As the official document said:

    To prevent developer code in OnInitializedAsync from running twice when prerendering, see the Stateful reconnection after prerendering section.

    2.You can use the IHttpContextAccessor.HttpContext.Response.HasStarted property to check whether the application is pre-rendering or not.

    @code{
        [Inject]
        protected IHttpContextAccessor httpContextAccessor { get; set; }
        
        protected override async Task OnInitializedAsync()
        {
            var isPreRendering = !this.httpContextAccessor.HttpContext.Response.HasStarted;
        
            if (!isPreRendering)
            {
                //call your service...
            }
                
        }
    }
    

    The HttpContextAccessor service should be registered by calling the AddHttpContextAccessor method in the Program.cs.

    builder.Services.AddHttpContextAccessor();
    

    3.Consider using OnAfterRenderAsync if you need to perform logic that should only run once after the component is fully rendered. However, be cautious since it runs after every render.

    @code{
        protected override async Task OnAfterRenderAsync(bool firstRender)
        {
            if (firstRender)
            {
                //call your service here
            }
        }
    }