virtual-dom
virtual-dom copied to clipboard
Wrong Msg tag used when using <img> onLoad event
Example
module Main exposing (main)
import Browser
import Browser.Events
import Browser.Navigation
import Html exposing (Html)
import Html.Attributes
import Html.Events
import Json.Decode
type Model
= Button
| Image
| Blank
type Msg
= ButtonMsg ButtonMsg
| ImageMsg ImageMsg
| BlankMsg BlankMsg
type ButtonMsg
= ButtonPressed
type ImageMsg
= ImageRendered
| ImageLoaded
type BlankMsg
= NoOp
| Reload
main : Program () Model Msg
main =
Browser.document
{ init = \_ -> ( Button, Cmd.none )
, update = update
, subscriptions = subscriptions
, view = \model -> { title = "", body = [ view model ] }
}
update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
case ( Debug.log "" msg, model ) of
( ButtonMsg ButtonPressed, Button ) ->
( Image
, Cmd.none
)
( ButtonMsg _, _ ) ->
( model
, Cmd.none
)
( ImageMsg ImageRendered, Image ) ->
( Blank
, Cmd.none
)
( ImageMsg _, _ ) ->
( model
, Cmd.none
)
( BlankMsg Reload, Blank ) ->
( model
, Browser.Navigation.reload
)
( BlankMsg _, _ ) ->
( model
, Cmd.none
)
subscriptions : Model -> Sub Msg
subscriptions model =
if model == Image then
Browser.Events.onAnimationFrame (\_ -> ImageMsg ImageRendered)
else
Sub.none
view : Model -> Html Msg
view model =
case model of
Button ->
Html.div []
[ Html.button [ Html.Events.onClick ButtonPressed ]
[ Html.text "Start" ]
]
|> Html.map ButtonMsg
Image ->
Html.div []
[ Html.img
[ Html.Attributes.src "https://picsum.photos/2000"
, Html.Events.on "load" (Json.Decode.succeed ImageLoaded)
]
[]
]
|> Html.map ImageMsg
Blank ->
Html.div [] [ Html.text "" ]
|> Html.map BlankMsg
Explanation
- The program above starts in the
Buttonstate and a button is shown in the view. - When that button is pressed, the program is transitioned to the
Imagestate. A random image is rendered in the view with aloadevent listener attached that should sendImageMsg ImageLoadedmsg once the image is loaded. - A msg from
Browser.Events.onAnimationFramecomes toupdatewhich allows to detect that the image was rendered and started to load. The program is then transitioned to theBlankstate. Theimgelement is destroyed and an empty view is shown. The image that was requested by theimgelement is still not loaded though - the request is not finished yet. - Finally (when the program is already in the
Blankstate) the image is loaded by the browser, and at this point we should receive theImageMsg ImageLoadedmsg. But if you look at the console, you'd see that we gotBlankMsg ImageLoadedinstead - which is totally wrong sinceImageLoadedis of typeImageMsgand can't be attached to theBlankMsgvariant of theMsgtype!
Furthermore, when compiled with the --optimize flag, this behavior can result into potentially dangerous and hard-to-debug issues because a wrong branch of the update function can be called causing some totally unrelated code to be executed. If you try the example above with the --optimize flag, the ( BlankMsg Reload, Blank ) -> branch will be called and the page will reload. I assume this is because ImageLoaded and Reload are both the second variant of their types, and since the type info is removed when compiled with --optimize, they are found by their index.
got similar issue:
I'm having run time exception in my elm app. I was not yet able to fully reproduce this in Ellie but I have a show case of that wrong message is dispatched: https://ellie-app.com/8Ld3MTGhzQba1
first page with input, when you submit it by pressing Enter while in it you navigate to second page.
Open a Debug/Console/Log and you will see Page 2 update function receive Blur event from input on Page 1
also it will execute last statement in update case for Page 2