Communicating from parent to child and child to parent

894 views
Skip to first unread message

Brian Hicks

unread,
Jun 27, 2016, 4:42:48 PM6/27/16
to Elm Discuss
Parents and children communication seems to come up a lot here and in #general on Slack. I wrote up a summary of my preferred way of communicating between parents and children: https://www.brianthicks.com/post/2016/06/23/candy-and-allowances-parent-child-communication-in-elm/ and https://www.brianthicks.com/post/2016/06/27/candy-and-allowances-part-ii-child-child-communication-in-elm/.

The thing I'm concerned with: how well does this pattern scale? Returning messages from the childs update function is all good, but does it work well when there are a lot of messages to be returned? Is there a better way to deal with that, and is there a "smell" that tells you when to switch patterns? I'm really interested in what the more experienced people here have done in this circumstance.

Wouter In t Velt

unread,
Jun 28, 2016, 6:12:23 AM6/28/16
to Elm Discuss
Thanks for sharing. The additional OutMsg from the update function was a real eye-opener for me.
I have no experience (yet) in scaling this.

One thing I find somewhat odd in your (second) example is that you define the OutMsg type in the Child:
Wouldn't it be more logical to always define/ manage Message types in the component that actually handles these Messages and has an update function for this?

In your example, the parent is the Owner of the OutMsg: the parent has separate update function(s) for dealing with each of the OutMsg types.
To prevent circular import references (parent importing child, and child importing parent), one would need to separate out parent and child into separate files (as in the - not 0.17 yet - example of the Players in the Elm docs).

That way, any child component of parent can import the OutMsg type (maybe ParentPublicMsg would be a more appropriate name). Then the parent exposes this type for any child that wishes to send out such a message. When you then expand OutMsg with, say AskForMoreAllowance, you add it to the parent-owned type + to the parent's update function. Children can be updated to respond to the new OutMsg type, but better still: any child component which is not updated will continue to work.

My specific use case: I have a list of children with a number of internal messages (e.g. updates on each child). One of the functions I need to implement is 'RemoveMeFromList', which is a button on each child. This message type can only be handled by the parent, through removing child from list and no longer rendering it in view.

Brian Hicks

unread,
Jun 28, 2016, 8:48:30 AM6/28/16
to Elm Discuss
The kind of message you describe in your example is definitely something that should be owned by the parent. It has more to do with the parent component than the child. It could definitely be moved to the child with a name change though. "Retire" or something like that.

The motivation of having the OutMsg on the child in general is to allow reuse of the child components. Take a "card" component, for example: you might have multiple components with varying content, but all producing the same message. You might also be using the component in multiple places. In that case, you would have to define the OutMsg on the child anyway, since otherwise you'd have a conflict between returning ParentA.InMsg and ParentB.InMsg.

There are multiple ways to structure this, but my experience tells me that the child should own all messages it consumes and produces.

Wouter In t Velt

unread,
Jun 28, 2016, 5:17:31 PM6/28/16
to Elm Discuss
Thanks. Guess it depends on specific use cases. If one child type is owned by many different parent types (example of button comes to mind) then having the child own the messages makes sense.
If on the other hand one parent type can have many different types of children (example: list with many different child types), having parent own the message is probably better.

BTW: Have you seen the Parts library? https://github.com/debois/elm-parts
I found it today, and also mentions a specific case of splitting internal messages from messages that parent should respond to. Still trying to figure out how it works and how to apply, but looks like it may be alternative approach to parent-child communication.

Mark Hamburg

unread,
Jun 28, 2016, 11:44:38 PM6/28/16
to elm-d...@googlegroups.com
On Jun 28, 2016, at 6:12 AM, Wouter In t Velt <wouter....@gmail.com> wrote:
>
> My specific use case: I have a list of children with a number of internal messages (e.g. updates on each child). One of the functions I need to implement is 'RemoveMeFromList', which is a button on each child. This message type can only be handled by the parent, through removing child from list and no longer rendering it in view.

This is a place where presentation (view) hierarchy and data (model) hierarchy mix uncomfortably. Removal from the list of children has little to do with the child unless there are other updates that accompany the removal that only the child understands. It is purely an update to the parent. This is different from the child needing a way to express "I've run out of money". That is based on an update to information local to the child. What muddles this up is that the remove button is often presented as part of the presentation of the child and, in The Elm Architecture, we are trained to think of the presentation as being built by a view function from model to HTML and hence if the presentation contains a removal button then remove must be a message destined for the model.

The Elm 0.16 architecture tutorial handled this by providing a separate address to which the remove message could be delivered.

The Elm 0.17 documentation at present is silent on the subject. There have been proposals for how to deal with this in an Elm 0.16 style but they haven't been officially endorsed and result in view functions with a return type of Html parentMsg as opposed to Html ChildMsg which is a little ugly. There are also suggestions to use extra return values from the update function (as the essays linked to in this thread do) but those result in the child update function containing entries that basically just serve to route the remove message up to the parent thereby contaminating the update function with a complexity that previously only affected the view function.

I've still been on Elm 0.16 because I've been waiting for key support, but I've been looking at 0.17 and one thing that does have me worried is that in getting rid of addresses may have increased coupling between the view hierarchy — driven by UX design concerns — and the model hierarchy which should be driven by data consistency concerns. While the default handling in the past for addresses led to a tight correspondence between the hierarchies, one could always work around it by providing an address along with a model (or as part of a model) wherever appropriate and it didn't feel too unnatural. One could probably do something similar by using routing functions(*) in much the same way as addresses in 0.16 but they feel like a fight against the intended use pattern.

But all of those judgments are suspended pending more complete documentation for these cases for 0.17.

Mark

(*) view : (msg -> rootMsg) -> model -> Html rootMsg

This could just use the standard Elm 0.17 routing instead (or in its implementation) but it stresses that the route isn't driven by the presentation but is rather information describing how to get messages to the relevant portion of the model.

To make it work well with lazy HTML, it really needs to be based on embedding the route into the model and only computing it at initialization time for that piece of the model.

Rindra Ramamonjison

unread,
Jun 29, 2016, 10:49:23 AM6/29/16
to Elm Discuss
Thanks again for sharing your thoughts on this. It is one of the hottest topics for the community right now. 

Part II was definitely showing the pattern in a clear way. I wish you clarify the ChildMsg type again in the second post for readers. 

I do not know either how this will scale but it would be nice to experiment also with point-to-point communication and with worse ways of doing the same thing. 
My hope is to find a simple yet generic way to handle communications. 

If the child also needs to send Cmd's, would you recommend using `Cmd.Extra`'s library way to unify commands and outgoing messages to parent component?

Mark Hamburg

unread,
Jun 29, 2016, 11:42:49 AM6/29/16
to elm-d...@googlegroups.com
In my app, I changed the standard update signature from:

msg -> model -> ( model, Cmd msg )

to:

msg -> model -> ( Maybe model, List msg, List notification )

Here was the reasoning for each of the changes:

1. Returning Maybe model: Laziness in view generation depends on not needlessly changing the data being passed to a view function. Originally, I was handling this by checking the results of sub-model updates for equality before updating the containing model. That approach, however, can run afoul of the fact that Elm doesn't like to compare functions for equality, so if your sub-model happens to contains a function, the equality comparison can result in JavaScript assertions. (Or at least it could in 0.16.) [*] So, instead, it seemed better to let update functions signal that they didn't change the model and maybe provided a good way to do that.

2. Lists of messages rather than a single command: This made the no command case easier to write — [] — and the batch function just wraps the list anyway.

3. Lists of notifications: This third parameter provides a way to pass information back up the hierarchy. For example, I have a notification that adds an error to a globally managed error list. Lower-level update functions can just observe that something went wrong, not update the model, and report the error up to the level where they are being collected. But this mechanism also allows the login logic to return a "LoggedIn userid" notification to the main app state manager.

I've contemplated merging the commands and the notifications to get things down from three to two but they do behave rather differently. Commands get annotated with additional routing as they move up the hierarchy and are expected to flow out to the app logic to interact with the outside world. In contrast, notifications are destined for upper levels within the model update process and can absorbed, transformed, or passed on as appropriate. The passed on part is important. Where a command will get mapped so that it's result comes back to the sender, a notification isn't expected to come back anywhere and hence doesn't need mapping unless we have to change notification types.

This approach has only been in place for a while so I'm not ready to give a definitive report, but it has seemed to clean up a lot of ad hoc logic.

Mark

[*] I've thought about whether Elm should fallback to essentially creation-defined equality — i.e.., values are equal if and only if they are the same JavaScript object — or offer an operator providing this test, but doing so would mean that various compiler optimizations could change the meaning of programs, so it's probably not a good idea.

Rex van der Spuy

unread,
Jun 29, 2016, 12:35:11 PM6/29/16
to Elm Discuss
Hi Everyone!

I'm confused about why this needs to be so complex?

For example, let's say you have a button child that needs to to communicate its state to its parent.
You could create a `currentMsg` property on the child button model like this:

type alias ButtonModel = { currentMsg : Msg }

Then when the child button is clicked, capture the current message on that same child model:

update message childModel =
  case message of
   Click ->
        { childModel 
            | currentMsg = message
        }

The parent has a reference to that child button in its model:

type alias ParentModel = { button : Button.ButtonModel }

Then in the parent's `update` function, there might be an `UpdateButton` message that updates the child button's state and does something based on the value of `button.currentMsg` 

 if parentModel.button.currentMsg == Click then -- do something useful

This is how I've handled all my child-to-parent communication in the last few apps I've built.

Can someone tell me if this is a bad idea or if there's a crucial part of the problem I'm not understanding?
Is there a use case where this would not work?


Rindra Ramamonjison

unread,
Jun 29, 2016, 9:27:49 PM6/29/16
to Elm Discuss
Hi Mark, 

Thank you for the input. I understand the need t differentiate between commands that goes to outside world and simple messages that goes to the component or its parents' update loop. 

For the later, is there a way to create a subscriptions out of the notifications? 

I believe the ideal solution is to convert children's notifications into
Child.Sub OutMsg

that a parent can subscribe to. 
Message has been deleted

Wouter In t Velt

unread,
Jul 1, 2016, 10:15:40 AM7/1/16
to Elm Discuss
Hi Rex,

I hope I interpret your setup correctly, but it looks like the flow is more or less:

  • user clicks a button inside child
  • this triggers child's update function
  • child's update function stores updates child model (with a message Click)
  • the parent can then access the new function by accessing the child's model

What I do not understand is: you mention an UpdateButton message that the parent responds to,

But what triggers this message?

Typically, the parent does not know or care about child model contents or child model updates. The parent receives child messages, but parent does not read child messages. Parent simply passes the messages on to the child's update function, and stores the updated child model.

 

This separation guarantees that you can modify code in your child component (e.g. change the child model, add new message types etc), and any parent will continue to work properly. No need to check or change code in any other component that imports the child.

If any parent 'looks inside' the model or the messages of the child, then this guarantee breaks down.

 

The discussion here (or at least my interest in this) is:

How do I communicate a message from a child to parent, while at the same time maintaining the integrity of the child's model (so no peeking by parents into child model)?

 

Rex van der Spuy

unread,
Jul 10, 2016, 9:11:53 AM7/10/16
to Elm Discuss


On Friday, July 1, 2016 at 10:15:40 AM UTC-4, Wouter In t Velt wrote:
Hi Rex,

I hope I interpret your setup correctly, but it looks like the flow is more or less:

  • user clicks a button inside child
  • this triggers child's update function
  • child's update function stores updates child model (with a message Click)
  • the parent can then access the new function by accessing the child's model
Yes, that's correct.

What I do not understand is: you mention an UpdateButton message that the parent responds to,

But what triggers this message?


The parent's view, which updates the child's view.

In this system I've been using, the parent checks a property on the child, and then decides whether it should react to that change.
You're correct that this requires the parent to know what that child's property is (For example, a button's state: Up, Over, Down, Clicked).
Is that the dependency you're trying to avoid?
 

Typically, the parent does not know or care about child model contents or child model updates. The parent receives child messages, but parent does not read child messages. Parent simply passes the messages on to the child's update function, and stores the updated child model.

 

This separation guarantees that you can modify code in your child component (e.g. change the child model, add new message types etc), and any parent will continue to work properly. No need to check or change code in any other component that imports the child.

If any parent 'looks inside' the model or the messages of the child, then this guarantee breaks down.

 

The discussion here (or at least my interest in this) is:

How do I communicate a message from a child to parent, while at the same time maintaining the integrity of the child's model (so no peeking by parents into child model)?


 Thanks for explaining that!
Reply all
Reply to author
Forward
0 new messages