Using Task in init

Flags are useful because it allows you to get an initial state before running the init function of the program. But it requires you to use JavaScript to get the initial state. It might be a good idea to be able to use a Task to get the initial state. This is useful if you want to get the viewport, time, etc. in init. This can be done by changing the type of init from this:

flags -> ( model, Cmd msg )

to this:

flags -> Task Never ( model, Cmd msg )

This allows you to get the initial viewport width and height by doing this:

type alias Model =
    { width : Float
    , height : Float
    }

init : () -> Task Never ( Model, Cmd Msg )
init _ =
    Browser.Dom.getViewport
        |> Task.map ( \{viewport} ->
            ( { width = viewport.width
              , height = viewport.height
              }
            , Cmd.none
            )
        )

This is better than using flags because you don’t have to deal with JavaScript’s impurity and runtime errors, and you can use it in Lamdera which doesn’t allow custom JavaScript code. It’s better than running a Task to get the viewport after the model is initialized because you can avoid handling the state where you don’t have the viewport size yet.

If you need this, you can do it with the current structure by marking the model as uninitialized.

type alias Size = 
    { width : Float
    , height : Float
    }


type alias Model =
    { size : Maybe Size 
    }

init : () -> ( Model, Cmd Msg )
init _ =
    (Model Nothing, Browser.Dom.getViewport |> Task.perform Initialize )

type Msg 
    = Initialize Browser.Dom.Viewport
    ...

update : Msg -> Model -> (Model, Cmd Msg) 
update msg model = 
    case msg of 
        Initialize viewport -> 
            (Model {width = viewport.width, height = viewport.height}, Cmd.none)
        ...

view : Model -> Html Msg 
view model = 
     Maybe.map viewForSize model.size 
     |> Maybe.withDefault (text "")

If you don’t like the Maybe you can use a flag isInitialized or you can make the Model a custom type like type Model = Initializing | Initialized State.

3 Likes

You can already implement this as a package.
https://package.elm-lang.org/packages/alex-tan/loadable/latest/ is one such example.

1 Like

This topic was automatically closed 10 days after the last reply. New replies are no longer allowed.