Bolero icon indicating copy to clipboard operation
Bolero copied to clipboard

Page Blinking issue in WASM mode.

Open OnurGumus opened this issue 4 years ago • 11 comments

A new problem has emerged with latest bolero 0.11. Steps to produce

  • Create a new application by using standard bolero template.
  • Run the application (You may need to install Microsoft.AspNetCore.Components 3.1.1 if you upgraded to .net core 3.1.1, this is another bug)
  • You will see the page loads fine then it becomes white and it loads again. Only in WASM mode @Tarmil this looks a bit serious :)

OnurGumus avatar Jan 16 '20 16:01 OnurGumus

Strangely it doesn't always happen. I am adding a gif here: bolero

OnurGumus avatar Jan 16 '20 17:01 OnurGumus

  • (You may need to install Microsoft.AspNetCore.Components 3.1.1 if you upgraded to .net core 3.1.1, this is another bug)

Ouch, yeah somehow the Bolero solution installs 3.1.1 but creates a package that depends on >= 3.1.0, I don't know how that didn't surface when testing the template :/ Sorry, I'll fix that.

  • You will see the page loads fine then it becomes white and it loads again.

Right, what's happening is that the page is statically rendered in HTML (because of the RenderMode.Static in Pages/_Host.cshtml), and then once the wasm has loaded, it is re-rendered by the client-side. This used to happen more smoothly, but I guess there have been changes in the rendering engine that made it clear the static content earlier or something like that. I think I'll remove the prerendering from the project template for now.

Tarmil avatar Jan 16 '20 22:01 Tarmil

@Tarmil here are my additional findings:

  • Unfortunately the same problem happens with ServerPrerendered mode as well as Static. And this is a big deal IMHO.
  • I tried a C# blazor application with WASM and working with Razor Pages with above prerendering modes. The problem doesn't occur there. So problem is with the way Bolero works.
  • Just as a side note, in your template you can use the newer syntax as
  <component type="typeof(HelloWorld.Client.Main.MyApp)" render-mode="ServerPrerendered"  />

assuming you add the tag helpers to the top

@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers

OnurGumus avatar Jan 17 '20 08:01 OnurGumus

@Tarmil I found the culprit. It's basically the async program thing causes this problem https://github.com/fsbolero/Bolero/commit/fa37b7e30e4e2ba9cd02d6eaeb32d62971b5dbc7#diff-42537e1b023213573ef3e20b1e6fa305

--removed
  override this.OnInitialized() =
        base.OnInitialized()
        let program = this.Program
        let setDispatch dispatch =
            this.Dispatch <- dispatch
        { program with
            setState = fun model dispatch ->
                this.SetState(program, model, dispatch)
            init = fun arg ->
                let model, cmd = program.init arg
                model, setDispatch :: cmd
    member internal this._OnInitializedAsync() =
        base.OnInitializedAsync()
-- added
    override this.OnInitializedAsync() =
        async {
            do! this._OnInitializedAsync() |> Async.AwaitTask
            let! program = this.AsyncProgram
            let setDispatch dispatch =
                this.Dispatch <- dispatch
            { program with
                setState = fun model dispatch ->
                    this.SetState(program, model, dispatch)
                init = fun arg ->
                    let model, cmd = program.init arg
                    model, setDispatch :: cmd
            }
            |> Program.runWith this

I am not sure what part of this causes the problem, but I think we don't need this async program feature as of now , blazor supports parametrized prerendering. Unless you found a better solution I recommend revert above change, since this flickering is a pretty annoying issue.

OnurGumus avatar Jan 17 '20 19:01 OnurGumus

On the second thought, I think we still need this feature if possible. In WASM mode, even if we prerender the data, we still need to reload the data before the first rendering occurs on browser side. But still flickering is just bad. A proper solution is required.

OnurGumus avatar Jan 17 '20 20:01 OnurGumus

@Tarmil another finding this there is a problem with this call: https://github.com/fsbolero/Bolero/blob/47e14bf8f8fa868f7bf09b3bb50de26bd323f698/src/Bolero/Components.fs#L155

Since elmish initially calls SetState this causes ForceSetState being called too early and eventually you call this.InvokeAsync(this.StateHasChanged) before the init phase. As a result OnAfterRenderAsync is called before OnInitializedAsync which is against standard lifecycle.

Perhaps you should first check if OnAfterRenderAsync is called before calling this.InvokeAsync(this.StateHasChanged).

OnurGumus avatar Jan 18 '20 05:01 OnurGumus

Thanks for the investigation @OnurGumus! I've been trying various fixes but so far the only way I've managed to reliably get rid of the blink is by reverting to synchronous OnInitialized. I'll publish a hotfix that does that, and then keep trying to see if there's a way to make OnInitializedAsync work because I'd really like to allow people to use AsyncProgram to do some async initialization.

Tarmil avatar Jan 19 '20 07:01 Tarmil

@Tarmil Another finding is You can also check if firstTime true for this call https://github.com/fsbolero/Bolero/blob/47e14bf8f8fa868f7bf09b3bb50de26bd323f698/src/Bolero/Components.fs#L203 There is no point in calling the same thing over and over again

BTW so far I found the only reliable way to initialize data on WASM browser is to use SetParametersAsync which happens before everything. There I was able to do my async loading and set it to a property of the class, then I was able to pass it to model. It's probably not the most natural way but it works.

OnurGumus avatar Jan 19 '20 09:01 OnurGumus

Fixed with 0.11

OnurGumus avatar Jan 19 '20 19:01 OnurGumus

@Tarmil , Good news, I have found a better way, just initialize elmish on OnParametersSetAsync Then then everything works as expected and we can keep AsyncProgram. I also believe Initialize is too early. With OnParametersSetAsync you also give some opportunity for parameters being set. It also allows you to write your own Initialization before elmish starts. So I suggest to go with it. Re opening the issue for the sake of book keeping.

OnurGumus avatar Jan 19 '20 20:01 OnurGumus

Though the bad part is, if you actually do any useful work in AsyncProgram, it flickers again :(

OnurGumus avatar Jan 19 '20 20:01 OnurGumus