Merlin04
Merlin04

Reputation: 2317

JSRuntime.InvokeVoidAsync in Blazor Server app calling multiple functions when I only tell it to call one

I have a Blazor server app with two buttons. The first button calls the MenuOpen method in C#, and the second calls the TestJSInterop method in C#. Here is the code:

@inject IJSRuntime JSRuntime;

<!-- buttons -->

@code {
    List<int> tabs = new List<int>();

    protected override Task OnAfterRenderAsync(bool firstRender)
    {
        JSRuntime.InvokeVoidAsync("monacoInit");
        return base.OnAfterRenderAsync(firstRender);
    }

    async Task OpenNewTab(string title)
    {
        int newId = await JSRuntime.InvokeAsync<int>("addEditorModel", new object[]
        {
            title, "test", "test", "test"
        });
        tabs.Add(newId);
        await JSRuntime.InvokeVoidAsync("addTab", newId);
    }

    async Task MenuOpen()
    {
        await OpenNewTab("testing");
    }

    async Task TestJSInterop()
    {
        await JSRuntime.InvokeVoidAsync("console.log", "test");
    }
}

In the Javascript, I've added console.logs to each method that's being called from C#. When I press the button to call TestJSInterop, I get the following result in the console:

test

call addEditorModel

call addTab: parameter is 0

call addEditorModel

call addTab: parameter is 0

When I press the button to call MenuOpen, I get this result:

call addEditorModel

call addTab: parameter is 1

call addEditorModel

call addTab: parameter is 0

call addEditorModel

call addTab: parameter is 0

I know that the OpenNewTab C# method is not being called multiple times, as when I put a breakpoint there and click the button to call TestJSInterop I do not reach the breakpoint.

The JSRuntime.InvokeVoidAsync call does work correctly in OnAfterRenderAsync.

Why am I getting this behavior?

Upvotes: 6

Views: 18453

Answers (1)

enet
enet

Reputation: 45646

I was trying to understand your partial code, but it seems as though I've failed. However, you should take into account the following:

  1. The OnAfterRenderAsync method is executed each time your component is being rendered. It is primarily used to initialize JavaScript objects. When you want to initialize your JavaScript objects (I guess only once, right ?), the code should be placed within an if statement checking that this is the first time:

    if( firstRender )
       {
           // Here's you place code that is executed only once
           // the parameter firstRender is evaluted to true only the first time 
           // the OnAfterRenderAsync method is executed
        }
    
  2. Only UI events, such as the click event, cause your component to re-render. If you need to re-render your component in order to see changes made otherwise, you'll have to call the StateHasChanged method

  3. If your app is pre-rendering ( render-mode="ServerPrerendered" ), your components are rendered twice, and thus you may see messages printed in the console window twice. This is OK.

Hope this helps... If you did not solve your issues, please post a complete repo of your code, and I'll try to help you with this.

Upvotes: 6

Related Questions