low_ghost low_ghost - 1 year ago 65
Javascript Question

Elm Effects Mapped to Nested Component

In this example (RandomGifPair), how is the update corresponding to NewGif actually wired to execute after the parent component fires

RandomGif.update act model.left
? It seems like the
RandomGif.update NewGif maybeUrl
needs to be manually fired somewhere. To be a little more explicit, RandomGifPair fires its Left update action and gets back a model / effect pair by manually calling RandomGif's update function. The returned effect is executed through the
Effects.map Left fx
, and that goes on to the getRandomGif function in RandomGif

getRandomGif : String -> Effects Action
getRandomGif topic =
Http.get decodeUrl (randomUrl topic)
|> Task.toMaybe
|> Task.map NewGif
|> Effects.task

which, as far as I understand it, will go on to fire the NewGif action which, due to the Effects.map, is now also tagged with Left. The only part of the picture I'm missing is how this action is kept within the scope of RandomGif and the action corresponding to the NewGif case of this update actually fired:

update : Action -> Model -> (Model, Effects Action)
update action model =
case action of
RequestMore ->
(model, getRandomGif model.topic)

NewGif maybeUrl ->
( Model model.topic (Maybe.withDefault model.gifUrl maybeUrl)
, Effects.none

when Main.elm only has the update function from RandomGifPair and thus has no case for NewGif.

I'm sure the answer lies in specific details of ports, Effects.map, forwardTo or Tasks that I'm missing.

For reference, here is an attempt to solve the problem in javascript that contains an entry in the upper update function for NewGif and manually calls RandomGif.update inside of it. Probably not the best way to try to understand elm...

Answer Source

All actions come in to your top-level update function. There's no way to scope actions to a sub-update function - they always come in at the top. So, most programs will manually route actions down to the lower update functions. That's what's happening here. All of the actions must go into RandomGifPair.update, and it's up to that function to a) call sub functions and b) store the results in the right spot in the state. It can be surprisingly cumbersome.

Here's the specific point in RandomGifPair.update that does the routing.

Line 42 says "oh this is a Left action? Give me the act that's inside of there. NOW you have your desired NewGif or RequestMore stored in act, and you know that it was bound for the left one. Line 44 calls the lower update function, which knows how to handle that. Line 46 stores the result of the lower update function in the model (by recreating the whole model with the new left and re-using the old right).

This is all obscured by the boilerplate around Effects. If you can understand how the actions flow first, then go back and apply the same logic to the Effects, I think that'll become clearer.