Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Wrong Msg tag used when using <img> onLoad event #162

Open
edkv opened this issue Dec 10, 2019 · 1 comment
Open

Wrong Msg tag used when using <img> onLoad event #162

edkv opened this issue Dec 10, 2019 · 1 comment

Comments

@edkv
Copy link

edkv commented Dec 10, 2019

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

  1. The program above starts in the Button state and a button is shown in the view.
  2. When that button is pressed, the program is transitioned to the Image state. A random image is rendered in the view with a load event listener attached that should send ImageMsg ImageLoaded msg once the image is loaded.
  3. A msg from Browser.Events.onAnimationFrame comes to update which allows to detect that the image was rendered and started to load. The program is then transitioned to the Blank state. The img element is destroyed and an empty view is shown. The image that was requested by the img element is still not loaded though - the request is not finished yet.
  4. Finally (when the program is already in the Blank state) the image is loaded by the browser, and at this point we should receive the ImageMsg ImageLoaded msg. But if you look at the console, you'd see that we got BlankMsg ImageLoaded instead - which is totally wrong since ImageLoaded is of type ImageMsg and can't be attached to the BlankMsg variant of the Msg type!

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.

@setdvd
Copy link

setdvd commented May 2, 2020

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants