diff --git a/src/Bolero/Components.fs b/src/Bolero/Components.fs index 483fbcc..eaafd94 100644 --- a/src/Bolero/Components.fs +++ b/src/Bolero/Components.fs @@ -194,8 +194,7 @@ and [] with _ -> () // fails if run in prerender ) - override this.OnInitialized() = - base.OnInitialized() + override this.OnInitializedAsync() = let setDispatch d = dispatch <- d program <- @@ -207,11 +206,28 @@ and [] id id (fun _ model dispatch -> setState model dispatch) id id - runProgramLoop <- Program'.runFirstRender this program - setState <- fun model dispatch -> - match oldModel with - | Some oldModel when this.ShouldRender(oldModel, model) -> this.ForceSetState(model, dispatch) - | _ -> () + + let finishInitialization() = + setState <- fun model dispatch -> + match oldModel with + | Some oldModel when this.ShouldRender(oldModel, model) -> this.ForceSetState(model, dispatch) + | _ -> () + + let updateInitState, initModel, loop = Program'.runFirstRender this program + runProgramLoop <- loop + + match this.StreamingInit with + | None -> + finishInitialization() + Task.CompletedTask + | Some init -> + task { + let! model, cmd = init initModel + updateInitState model cmd + finishInitialization() + } + + member val internal StreamingInit : ('model -> Task<'model * Cmd<'msg>>) option = None with get, set member internal this.InitRouter ( diff --git a/src/Bolero/Program.fs b/src/Bolero/Program.fs index 8c83243..eb4570b 100644 --- a/src/Bolero/Program.fs +++ b/src/Bolero/Program.fs @@ -23,8 +23,48 @@ module Bolero.Program open System.Reflection +open System.Threading.Tasks open Elmish +/// +/// Create a simple program for a component that uses StreamRendering. +/// +/// The model that is shown initially. +/// Load the model to be stream-rendered. +/// The Elmish update function. +/// The Elmish view function. +let mkSimpleStreamRendering + (initialModel: 'model) + (load: 'model -> Task<'model>) + (update: 'msg -> 'model -> 'model) + (view: 'model -> Dispatch<'msg> -> Node) + : Program<'model, 'msg> = + Program.mkSimple (fun (comp: ProgramComponent<'model, 'msg>) -> + comp.StreamingInit <- Some (fun x -> task { + let! model = load x + return model, Cmd.none + }) + initialModel) + update view + +/// +/// Create a program for a component that uses StreamRendering. +/// +/// The model that is shown initially. +/// Load the model to be stream-rendered. +/// The Elmish update function. +/// The Elmish view function. +let mkStreamRendering + (initialModel: 'model) + (load: 'model -> Task<'model * Cmd<'msg>>) + (update: 'msg -> 'model -> 'model * Cmd<'msg>) + (view: 'model -> Dispatch<'msg> -> Node) + : Program<'model, 'msg> = + Program.mkProgram (fun (comp: ProgramComponent<'model, 'msg>) -> + comp.StreamingInit <- Some load + initialModel, []) + update view + /// /// Attach `router` to `program` when it is run as the `Program` of a `ProgramComponent`. /// diff --git a/src/Bolero/ProgramRun.fs b/src/Bolero/ProgramRun.fs index cc2c40b..a84ad7a 100644 --- a/src/Bolero/ProgramRun.fs +++ b/src/Bolero/ProgramRun.fs @@ -105,8 +105,17 @@ module internal Program' = reentered <- true setState model dispatch - fun () -> + let mutable cmd = cmd + + let updateInitState m cmd' = + setState m dispatch + state <- m + cmd <- cmd @ cmd' + + let run () = cmd |> Cmd.exec (fun ex -> onError ("Error intitializing:", ex)) dispatch activeSubs <- Subs.diff activeSubs sub |> Subs.Fx.change onError dispatch processMsgs () reentered <- false + + updateInitState, model, run