Tewr / BlazorFileReader

Library for creating read-only file streams from file input elements or drop targets in Blazor.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

DragNDrop SSB Race Condition

srowan opened this issue · comments

  • Server side blazor
  • Similar code to the dragndrop example
  • Getting the following error randomly on refresh (about 30%-50% of the time):
      Unhandled exception rendering component: Could not find 'FileReaderComponent' in 'window'.
Error: Could not find 'FileReaderComponent' in 'window'.

The error occurs on: RegisterDropEventsAsync(); :

       protected override async Task OnAfterRenderAsync()
        {
            labelFilesReference = fileReaderService.CreateReference(labelElement);
            await labelFilesReference.RegisterDropEventsAsync();
        }

I do have InitializeOnFirstCall enabled, but I also tried this as well:

       protected override async Task OnAfterRenderAsync()
        {
            await fileReaderService.EnsureInitializedAsync();
            labelFilesReference = fileReaderService.CreateReference(labelElement);
            await labelFilesReference.RegisterDropEventsAsync();
        }

... and still received the error (same frequency). Adding Task.Delay did not help.

Retrying when it fails fixes the issue, which I think is probably evidence that there is a race condition here (which could mean the problem can only be replicated at very low latency e.g. local development).

This works:

        protected override async Task OnAfterRenderAsync()
        {
            labelFilesReference = fileReaderService.CreateReference(labelElement);
            try
            {
                await labelFilesReference.RegisterDropEventsAsync();
            }
            catch (JSException ex)
            when (ex.Message.Contains("Could not find 'FileReaderComponent' in 'window'"))
            {
                // failed race condition, just try again
                await labelFilesReference.RegisterDropEventsAsync();
            }
        }
commented

Thank you for your report.

I suspect something like prerendering
dotnet/aspnetcore#11876 but it's quite odd, doesn't really fit the bill. However, the drag and drop is the only example which does jsinvoke in afterrender.

Could you try disabling the prerendering and see if it helps? from https://docs.microsoft.com/en-us/aspnet/core/blazor/state-management?view=aspnetcore-3.0:

To disable prerendering:

  1. Open the _Pages/Host.cshtml file and remove the call to Html.RenderComponentAsync.
  2. Open the Startup.cs file and replace the call to endpoints.MapBlazorHub() with endpoints.MapBlazorHub<App>("app"). App is the type of the root component. "app" is a CSS selector specifying the location for the root component.

If this can be confirmed, I should probably add an alternative setup method for SSB in the demo project ( webBuilder.UseStaticWebAssets() etc), still, setting up js events with prerendering is a bit of a hassle

I actually already have pre-rendering disabled. I never had a problem with the loading time of SSB (way faster than any angular application I've ever written) and it only caused me headaches :)

commented

Effectively, I can reproduce this without prerendering so please disregard my previous comment. The problem is, like you say, a race condition in the FileReaderJsInterop.EnsureInitializedAsync method which happens occasionally, when the browser server has cached everything really well and executes the code which comes after faster than the script blob has been loaded in the browser context.

A possible fix that I've tried which works is to block asyncronically for a few ms and check if the script has been loaded. I find this fix unnerving because I have set script.async to false, and despite this, the script is apparently not always loaded when the eval ends...