Elm-remotedev - Elm ↔ Redux DevTools Extension Integration

Hi!

I just released a basic integration of Elm with the wonderful Redux DevTools Extension. Some useful features Redux DevTools has that Elm’s current builtin Debugger doesn’t:

  • Inspect the whole message value.
  • View a diff of the old model vs the new model after every update. (This has been most useful for me while working on an app with a huge model!)
  • Filter messages by substring match.
  • Visualize the state as a graph.

Here’s a demo of some of the features:

Demo GIF

I’ve only integrated sending the message and model to the extension; time travel is not supported at the moment.

Here’s the repository with the instructions to set this up in your dev environment: https://github.com/utkarshkukreti/elm-remotedev.

Let me know if you try it out!

35 Likes

Wow this is amazing! I’ve been wanting so long for the elm debug tools to have the ui/features of the redux-dev-tools. Well done!

5 Likes

Awesome, so cool to see this!

I can imagine that the diff would be most helpful (vs. what Elm 0.18 already provides in debug mode.)

Rock on!

3 Likes

This is really great. Thanks a lot! I was recently thinking how awesome something like this would be. Especially for seeing the whole message values!

1 Like

Looks really cool! Patching the core libraries seems tedious though. It would be neat if Elm exposed some debugging hooks, so that the debugger UI could be developed (and enabled) separately from Elm.

I usually use continuous delivery, which means you’re supposed to deploy the same artifacts to different environments. Having to configure the --debug flag at compile time hurts this process. If I want the testers to be able to send me exports of their run I need to compile my app with debugging enabled, but I don’t want to deploy my code with debugging enabled in production. Right now, I solve it by having debug enabled but hiding the elm-overlay with some CSS, based on some runtime configuration. If we had hooks instead, I could choose not to include the debugger.

3 Likes

I gave this quick try as it would certainly be a major addition to Elm. However while it worked from the homescreen, I quickly ran into this with our main app after navigating

43

Happy to connect somehow if you want to work out what is happening with this codebase

That’s strange. Could you click on main.js:8, then click on {} to pretty print the minified code and post a screenshot of the highlighted portion of the code (where the error originated)? I’m on Slack as well (same username), so feel free to ping me there whenever you’re on.

I think I found the problem. Could you try loading this file instead of the rawgit URL and see if that fixes it?

Yes! This was my biggest pain point with the official debugger.

1 Like

Yep, that fixed it :slight_smile:

1 Like

That would be cool but I’m not expecting it any time soon. This is a hard problem IMO and would require a lot of exploration to have a good API.

Fortunately my integration only requires 1 line patch so it should be worth it for the benefits of Redux DevTools vs Official Debugger for many people. (It certainly was worth it for me to develop the whole integration!)

I peeked into 0.19 and saw that the debug vs non-debug output will be significantly different in it. There are multiple optimizations that would only be enabled without --debug and the Debug module will be completely inaccessible without --debug.

Awesome! I’ve committed the fix. rawgit.com’s CDN will probably pick up the new version in a few hours.

2 Likes

Nice! I really think using the redux devtools is a better approach to debugging than trying to build your own :slight_smile:

4 Likes

This is awesome. Was wondering if it would be possible to do this more like the redux way a middleware instead of modifying the Elm core. Thought about something like:

main : Program Never Model Msg
main =
    Html.program
        { init = init
        , update =
            update
                |> withMiddleware portMiddleware
        , view = view
        , subscriptions = \_ -> Sub.none
        }


withMiddleware :
    (msg -> model -> ( model, Cmd msg ))
    -> (msg -> model -> ( model, Cmd msg ))
    -> (msg -> model -> ( model, Cmd msg ))
withMiddleware middleware upd msg model =
    let
        ( updatedModel, cmd ) =
            upd msg model

        ( mwUpdatedModel, mwCmd ) =
            middleware msg updatedModel
    in
        ( mwUpdatedModel, Cmd.batch [ cmd, mwCmd ] )


portMiddleware : Msg -> Model -> ( Model, Cmd Msg )
portMiddleware msg model =
    ( model, sendUpdate (encodeUpdate msg model) )


port sendUpdate : Value -> Cmd msg


encodeUpdate : Msg -> Model -> Value
encodeUpdate msg model =
    Encode.object
        [ ( "model", encodeModel model )
        , ( "msg", encodeMsg msg )
        ]

The problem is now you’d have to write encoders for your Msg and Model types. Would be cool to try to do something with parsing the output of toString maybe, to have a sort of generic encoder.

Anyway, really cool stuff!

I thought about this but writing an encoder for the Msg and Model for the app I’m working on will be a deal breaker. There are dozens of different messages and fields in the model. The encoder will probably a thousand lines long.

Parsing toString's output might be possible but if I ever want to implement the Time Travel feature in Redux DevTools, it would also require a decoder from String to Model and Msg. In the end I decided to just patch 1 line of code in elm-lang/core.

2 Likes

I don’t think it’s that practical to do it with ports, although you’re modifying generated code it seems like the best option right now is to patch the JS file.

1 Like

Yeah, I totally agree. Writing encoders would not be scalable, and I didn’t even think of the decoder aspect of time travel, which would only make it worse.

1 Like