Duplicate, Message, or Update? Contexts in Elm Components

Imagine: you’re implementing your Elm app. It’s getting bigger all the time, and you’re happy that it’s growing. Hooray! But now you need to make HTTP requests… from a child component. What to do? You don’t want to duplicate the HTTP config as a field on the user everywhere, right? But likewise, you don’t want to forward actions to the parent just to make HTTP requests, right? What’s the way to solve this while sticking with the best architecture for your app?

Three Options

We’re going to assume that you need to scope all your requests with a user and token. Something like this:

type alias Authentication = { user : String , token : String }

You already have this information stored on a field in your Model :

type alias Model = { auth : Authentication } -- plus the rest of your fields

But you’ll eventually need to build child components that will need this config too. You have three options here: duplication, child messaging, or value passing.

You could stick Authentication into the child model everywhere you need it. But this means you’re going to have some issues because the data is duplicated everywhere. And what if you need to change the values of Authentication after the initial authentication? It doesn’t scale well.

You could also send messages from the children to the parent. But sending messages to make effects to pass back to the children… that has a nasty smell. First of all, you’ll be moving the implementation of your child out of the child. That means you’re going to be doing shotgun surgery, which is best avoided. More importantly: Cmd s are how we do external requests, not message passing. Adding a layer of indirection will make the code harder to reason about.

What if we made Authentication an argument to update ? Something like this:

update : Authentication -> Msg -> Model -> (Model, Cmd Msg)

There’s no duplication of data here! The update functions that need the context will get the context. Changing authentication parameters is as simple as passing the updated data from your parent. If their children need Authentication , just pass it down to them.

There’s also no mixing of Msg and Cmd . This will help out a great deal with clarity of reasoning. When you issue your Cmd , the return value is a Msg in the current component. No more, no less. Parent components only need to pass on messsages that they would already.

In fact, this pattern can extend anywhere you need a little more context about what you’re doing. If you need a list of siblings, for example, you can pass it into update .

Now you know how to pass a contextual argument into an update function. More relevant: you know when to do it. Next time you need to issue a Cmd with context (like an HTTP request) you’ll know what to do.