Hyperapp: app(props, container) VS props.container

Created on 20 Oct 2017  路  24Comments  路  Source: jorgebucaran/hyperapp

Which want do we want?

app({
  state,
  view,
  actions,
  container|root
})

VS

app(
  {
    state,
    view,
    actions
  },
  container
)

Revival of #410.

Discussion

Most helpful comment

At this point I鈥檇 rather leave this the way it is than ping pong back and forth with breaking changes.

All 24 comments

I preferred the first option, where all of the app's config is in one object.

I don't really have a strong preference, but a slight leaning toward the second form. Because the container is not really part of the app's logic anyway. I can see situations (in particular in testing) where you get the app-definition from one place, and the container from someplace else. Combining them to execute the app is slightly easier and more natural in the second form, IMO.

The obvious con is that it makes writing HOA a little more complex. But only a very little. And the way I see it, HOA are uncommon and complex enough things anyway, we can expect a HOA author should know what they're doing.

Actually (just thought of this), even for writing HOA the second form might be helpful -- in case you're writing a HOA which takes care of setting up a container for you (for multiple apps, apps as stateful components, whatever...)

Still the difference is so small it's barely worth arguing about 馃槢

I support the second form as well,
It keeps the real app logic in its own object and makes it easier to switch the root.

Slight preference for option 2, but not strongly.

My only opinion on this matter is that container is not a part of the app wiring like view, actions and state are, so by that logic it makes sense that it is delivered separate from them.
container is also an optional argument with a reasonable default, which also fits a common pattern in JS to have such parameters following mandatory parameters to a function, so that you can easily just leave them out if not needed.

React's render() works this way too.

render(element, container)

This is a plus for newcomers from the land of React.

I find the first one more pleasing to look at when you are using all the props, because of how it's formatted.

On the other hand, you don't necessarily always use a specific root|container.

but why should one compare it to React? It is not all that good.

They both make sense.. I rarely use root|container anyway (just let it fallback to document.body) so I don't really have enough experience with this to comment. The first looks more compose-able the second looks more like how other frameworks do things (namely react).

At this point whicever one saves the most bytes/or is the more performant option. I don't mind either API as long as we pick one!

I think we should go with whatever makes it simple for two type of usage

a. Basic usage: I think for most users both options are simple.
b. App compose usage: I think the 2nd option may cause issues for people using/building HOA tools I guess.

So I'm now leaning towards option1 because it may increase cred in the functional programming world.

@rajaraodv Were you thinking of any issue in particular?

@zaceno Actually (just thought of this), even for writing HOA the second form might be helpful -- in case you're writing a HOA which takes care of setting up a container for you (for multiple apps, apps as stateful components, whatever...)

Could you elaborate please? 馃檹

@rajaraodv Were you thinking of any issue in particular?
Nope. I am just thinking about others talking about some issue while writing HOA. I myself am not aware of it.

@rajaraodv it would be nice to use normal function composition and functional libraries when building HOAs.

I'd like a final answer to this soon, so I know if I need to update all my HOAs or not 馃槣

@zaceno Actually (just thought of this), even for writing HOA the second form might be helpful -- in case you're writing a HOA which takes care of setting up a container for you (for multiple apps, apps as stateful components, whatever...)

Could you elaborate please? 馃檹

Hehe not even sure it makes sense. Just a notion that struck me. Was thinking if like you were using hyperapp to write a standalone ui component (generic, not specifically for any framework). Then you'd be given the container by the user of your thing. Then it would very slightly more convenient just to toss that on the end of the app call, rather than first assign container in your props and then call app.

Again: my feelings on this are not strong. Just trying to brainstorm around scenarios where a custom container would be used at all. Normally no one would use it anyway and it wouldn't matter.

I think it all depends on what we want to optimize for. Do we think this feature will be most often used to add Hyperapp to an existing SPA? If so, then the second form will be more familiar to devs that already know the tech being used on the rest of the app, likely React or other libraries with similar APIs.

However, this has a very different impact on library/HOA authors. With the first form you are already required to pass props to the next app function, so you would have to intentionally leave out container/root to break things. OTOH with the second approach you have to make a conscious effort to do the right thing by passing the second argument. Option 1 takes effort to get it wrong and option 2 takes effort to get it right. This could get annoying since most function composition helpers are designed for single argument functions. Which means library authors are rolling more of their own code, where bugs can creep in 馃悶

Here's a SO answer that goes into some of the details about why this is an issue: https://stackoverflow.com/a/28314380

If I am not mistaken, this is how HOAs look like.

import { h, app as core } from "hyperapp"

const app = reactor({ ... })(
  devtools({ ... })(
    logger({
      log(prevState, action, nextState) {
        // ...
      }
    })(core)
  )
)

app(
  {
    state,
    view,
    actions
  },
  document.getElementById("root")
)

From a user experience POV, going with either app(props, container) VS props.container make no real difference to me, other than the extra indentation. 馃槈

Now, HOA authors will need to pass along the container to the app like some of you pointed out, so there's that.

I dont know the status of the discussion, but for me, the HOA excuse is not real and serious.
Some weeks ago, I presented on slack my HOA snippet with the following features : scoped utilities, deeply modules ehancement, ehanced app function, etc...

And here is the revision to make it work with the container as second parameter

@@ -3,17 +3,17 @@
 function HOA(app) {
   /* HOA utilities */

-  return function enhancer(props, isModule) {
+  return function enhancer(props, container, isModule) {
     /* HOA on module props too */
     for (var name in props.modules) {
-      props.modules[name] = enhancer(props.modules[name], true)
+      props.modules[name] = enhancer(props.modules[name], container, true)
     }

     /* HOA internal */
     /* ... */

     /* HOA on module return an object */
-    return isModule ? props : app(props)
+    return isModule ? props : app(props, container)
   }
 }

So, this is not really a surprise for most of you, but I am in favor of the second version

app(
  {
    init,
    state,
    view,
    actions
  },
  container
)

Because I have two wish that match with the previous statement.

1. I want to use module and app props like the same things, Yes!

const Counter = {
  state: {}, 
  actions: {},
  view(state, actions) {}
}

app(Counter, container)

app({
  modules: { Counter }
  state: {}, 
  actions: {},
  view(state, actions) {}
}, container)

2. I want to use Hyperapp as a candidate to create WebComponents (because we are creating WebComponents at my company)

<body>
  <cool-counter id="counter1"><cool-counter>
  <cool-counter id="counter2"><cool-counter>
</body>
document.querySelectorAll('cool-counter').forEach(
  container => app(Counter, container)
)

This is pretty naive, but I show you two use cases in favor of the container as second parameter.

Is this still an open discussion? It seems like option two has been in long enough to close this now.

@okwolf I was hoping to revisit again later. I would like to know if everyone's opinion is still the same or not after modules gone.

Module was just a sugar for wiring imported state and actions in the same namespace in one line. The use case is unchanged.

Having to define the App props somewhere and attaching it to the dom elsewhere is still a good pattern for me.

At this point I鈥檇 rather leave this the way it is than ping pong back and forth with breaking changes.

any solution, @JorgeBucaran ?

(props, container) winner?

@dmitry-vakhnenko Yes, that's what it looks like.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

jorgebucaran picture jorgebucaran  路  4Comments

jscriptcoder picture jscriptcoder  路  4Comments

guy-kdm picture guy-kdm  路  4Comments

zhaotoday picture zhaotoday  路  3Comments

jacobtipp picture jacobtipp  路  3Comments