Hyperapp: Use superfine as a dependency?

Created on 6 Mar 2018  ·  49Comments  ·  Source: jorgebucaran/hyperapp

Discussion Wontfix

Most helpful comment

All 49 comments

Should we use it as a devDependency so it’s still all bundled into a single dependency for users to install?

Hmm? I don't think that's how it works. 🤔

Oh yeah, that would only work for the UMD build. 🙃

I think it would be a good thing! But since youve been opposed to this in the past, I’m curious: what changed your mind?

I think it's better to have to separate things. It makes it easier for people to contribute to one or the other, plus it avoids duplicate code (especially if you're back-porting all updates to hyperapp to ultradom).

How would that look? I assumed the reason you kept them separate until now was that hyperapp needed to be able to hook deeper into v-dom to make it smaller & easier to customize (more tailored for hyperapp, and not generic)?

@mrozbarry I found your comment a bit ambiguous: Do you mean “separate things” as in “Hyperapp should keep its own vdom implementation separate from Ultradom so they are free to diverge” or did you mean: “Hyperapp should reuse a separate vdom (Ultradom) rather than implement its own”

@mrozbarry sorry, reread your post. The end made it more clear you meant the latter (... right?)

Are there any downsides to this other than no longer having a single self-contained source file you can import the Hyperapp module from?

If Ultradom become a dependency, I would want to add on table the fact of making Hyperapp more like a state manager and Ultradom the renderer.

@SkaterDad True. For this to work, I would need to complicate Ultradom's API a bit more.

Now

patch(VNode, Element)

After

patch(VNode, Element, { resolveNode, proxyEvent, ...other, ...options })

I see this change as a good thing as it reduce repetition.
Regarding ultradom's API change, isn't it possible to rewrite render() to re-use ultradom's patch() as-is?

This wouldn't hurt performance by pulling in a more general-purpose Ultradom, correct?

@okwolf No, it wouldn't according to js-framework-benchmark and common sense, but it would definitely increase the bundle size considerably: 100 bytes give or take.

@Mytrill Yes, totally possible.

Ultradom would need a new feature, to support sub-views, right? I hope we can make it general somehow, so it's not too tightly coupled to hyperapp's architecture and state management.

EDIT: Sorry -- I see you already explained how above, with resolveNode, proxyEvent et c. 👍

I think one of the beautiful things about Hyperapp is that everything is kept super simple, easily readable and in a single file. While I agree it reduces repetition I would suggest to keep everything as simple as possible, which is what is unique about Hyperapp vs other, much more bloated libraries.

It is so cool when a core with no dependencies :)
What about merging ultradom with hyperapp and expose patch function via named export together with h and app?

@jimfranke I agree that having it all in a nice-n-tight little package you can easily read and comprehend is a great feeling. The other side of the coin, though, is Ultradom.

Up until recently, Ultradom has been compatible with Hyperapp's vdom, which has been a great "safety net". The simple architecture and state-management is at once one of the most appealing and scary things about investing time in Hyperapp. There are no computed properties, no dynamically instantiable models, no stateful components. Many newcomers ask about those and similar things -- presumably with this question in the back of their mind: "I like this - it's simple - but how far can it take me before I need more?".

I've wondered that myself plenty of times, and the thing that always made me feel safe digging in is the knowledge that my biggest investment is in the view/component logic -- which is already (well... was) compatible with Ultradom. So if I ever come to a breaking point, I can quite painlessly replace/mash-up just the state management with something that does what I want.

So, what I'm afraid will happen if we don't do this, is that Ultradom will fall behind or grow apart from Hyperapp, and all my code will be locked in to Hyperapp with it's beautiful, scary state management.

UPDATE:
Of course, I can fork Hyperapp and do whatever -- but if Hyperapp is built on Ultradom, then there will be a community around Ultradom, with resources bult for/on top of it (many probably also useful in Hyperapp-apps).

UPDATE II: Please note: I'm not saying that Hyperapp's state-management is necessarily inadequate for complex apps. I'm just saying that's a concern I've had (and I've heard plenty others express).

Thanks, @zaceno. Ultradom should continue to be compatible with Hyperapp's VDOM model.

UPDATE: FWIW whichever way we choose here, I am committed to maintaining both Hyperapp and Ultradom and keeping them always in sync feature-wise.

I think the two projects should be separate, but trivial to use both together. You are in control of the API on both sides, and we'll just cheat to make it perfect for us.

Sometimes two things are artificially separated and makes the code worse, I don't think it is true in this case. State management and dom rendering are very different concerns.

I think the two projects should be separate, but trivial to use both together.

I totally agree.. but isn't that nearly the definition of a dependency?

The _zero dependency_ argument doesn't really hold much weight in this decision, for me at least. From a users perspective, it makes little difference wether the codebase is split over 1 or 100 files/repos. So long you can still run npm i hyperapp and import { app } from 'https://unpkg.com/hyperapp?module' and the code in the various locations is comprehendible, then all is good.

The advantage I see of employing ultradom as a dependency is; less duplication of code (which is surely going to free up time for @jorgebucaran to make more interesting things), better seperation of concerns (state and dom management are different enough responsibilities to warrant two libs that do their job really well like @Pyrolistical says) and finally, it demonstrates how flexible the vdom implementation is, which might encourage others to play with alternative state management patterns (like @zaceno has been doing) which might lead to new ideas and improvements.

TLDR; I don't think it makes much difference 😅 zero dep is (nice, but somewhat) a vanity claim (much like size to a certain extent) and has very little to do with practicality.

Yep, also zero dep is a lie. What is happening here is you are including a hacked version of ultradom lol. So work with it as the true dep it really is

Sorry for lying to everyone, @Pyrolistical is right, Hyperapp is basically a hacked version of Ultradom. 🤣

Naive question:

How hard would it be to to maintain Hyperapp in parallel with Ultradom?

Thanks, :)

@russoturisto It's what I've been doing all along, so not extremely hard.

Just curious, Ultradom is 1KB and Hyperapp is 1KB. But Hyperapp is doing so much more than Ultradom. So I'm wondering if taking the vdom out of Hyperapp and using Ultradom will increase the over all size of the two together. That said, Hyperapp will be really little, so that would give you some wiggle room to improve features without worrying about the 1KB file limit.

@rbiggs Depends on how we do the counting. Hyperapp has always been something like 1.3 kB ~ 1.7 kB — it used to be a bit more when the router was included by default back in the old days.

Nowadays HA is closer to 1.5 kB. Ultradom alone is about 1.2 kB, which tells me that Hyperapp's built-in state management is not more 400 bytes or so. Splitting things up would change that as we would somehow gain an extra 100 bytes of monosaturated fat.

…some wiggle room to improve features without worrying about the 1KB file limit.

I am no longer that fervently obsessed about the file size and as many of you may have noticed, I've been improving the code readability at the expense of more bytes here and there. I am more open to changes than I was ever before. I have even changed the project's description in some places.

screen shot 2018-03-08 at 14 03 16


I want to protect the perceived simplicity of the project and would like to continue doing so, so I am calling off splitting up Hyperapp to introduce Ultradom as a dependency. I will keep both projects internal VDOM details as similar as possible though, so @zaceno I got you covered! 👋

I want to reconsider this.

Why not both? If you are OK with a more complicated build, you can always ship 2 flavours of HA. One without any vdom ~400b , and another with ultradom preintegrated ~1.5kb.

  • hyperapp.min.js
  • hyperapp.with.ultradom.min.js

You can even just sell the preintegrated one, and other "provide your own vdom" is for advanced users.

Might as well, the algos are essentially the same, right? Just have Ultradom as a dependency for Hyperapp. The user wouldn't even notice. At build time Ultradom would get bundled with Hyperapp automatically. Bada-bing, bada-boom!

As a plus, Ultradom stays focused on the virtual dom patching, which you can work on separately to improve render times. And Hyperapp is just the important things that it does: TEA-inspired state management in JavaScript. That just feels DRYer.

Only thing, when you make changes to Ultradom, you'll want to test it with Hyperapp before committing so you don't get blindsided by an egregious bug affecting Hyperapp. You'ld want to create a Hyperapp project that does some dynamic lists, etc. Throw in the new Ultradom. If it runs as expected, check in Ultradom.

To me, preserving the same code in two different projects does make sense.

If Ultradom is maintained under the Hyperapp community. It will be part of the project, and I will see not point against it.

@Pyrolistical Thanks for the suggestion, but I am definitely going to stick with a simpler build. Either integrate ultradom or not.

@rbiggs Yes, they're essentially the same.

@Swizz Maybe, we'll see. Thanks for your suggestion.

Or remove (v)dom from Hyperapp :roll_eyes:

import { app } from "hyperapp"
import { h, patch } from "ultradom"

const state = {
  count: 0
}

const actions = {
  down: value => state => ({ count: state.count - value }),
  up: value => state => ({ count: state.count + value })
}

const view = (state, actions) => (
  <div>
    <h1>{state.count}</h1>
    <button onclick={() => actions.down(1)}>-</button>
    <button onclick={() => actions.up(1)}>+</button>
  </div>
)

const run = (state, actions, view, element) => (
  app(state, actions, 
    (state, actions) => { 
      element = patch(view(state, actions), element)
    }
  )
)

const main = run(state, actions, view, document.body)

@Swizz Cool, but not interested! 😄

Or remove (v)dom from Hyperapp 🙄

Idea for a fork then?

@brodybits do it

_UPDATED: Starting a rewrite (fork of the API)_ with a couple demos:

Next steps:

These demos are extremely simple but I think they demonstrate the pure state/action management API working with multiple VDOM libraries and anticipate this idea will work without problems on larger-scale applications and React Native. Could be a nice replacement for Redux as discussed in https://medium.com/hyperapp/hyperapp-for-redux-refugees-2507c9dd1ddc.

@hyperapp people I hope you guys will consider supporting this idea within the existing @hyperapp organization, for the sake of quicker adoption by the user community.

I still need to checkout @brodybits work to get a better understanding of the idea.

Reading previous comments, namely this above, just made want to try one dream I've had for a long time, which is having hyperapp state management standalone to be used with any other view library…

Or remove (v)dom from Hyperapp 🙄
Idea for a fork then?

So I just hacked it last night and removed the DOM part from hyperapp, and added a very simple redux-like interface for listening to changes, check it out here on the hyperstate branch (tests are passing):

https://github.com/acstll/hyperapp/blob/hyperstate/src/index.js

You can do:

const store = app(initialState, actions) // regular hyperapp actions

store.listen(state => {
  expect(state.value).toBe(2)
})

store.up()

Does this make any sense? Before I may think of turning this into a package and give it a name, I'd love to hear your opinion.

@acstll @brodybits Here's a similar idea (same maintenance savings) with a slightly different approach.

https://github.com/jorgebucaran/ultradom/issues/108

Hey, that's the other way around, and I like it more…

If I understand correctly, hyperapp would be refactored internally, and then export the current h and app functions, but then also patch and state (or createStore or similar), right?

So you can use hyperapp like up until now:

import { app, h }  from 'hyperapp'

app(state, actions, view, container)

or just use state management

import { createStore }  from 'hyperapp'
// import any view library

or just DOM patching (as in ultradom now)

import { h, patch }  from 'hyperapp'
// use any state management you like

You don't have to give up one-fileness and, as you said, ultradom could also be maintained as something more experimental (things could be backported to hyperapp when ready).

Is this something? 🤔

Yeah, exactly the other way around. 😄

I am still exploring options, though.

If I understand correctly, hyperapp would be refactored internally, and then export the current h and app functions, but then also patch and state [...]

I do also like the idea that Hyperapp could act as a set of functions that people could use individually with help of "tree shaking", assuming I understand this correctly. But I would personally favor keeping the state management part completely independent of the (V)DOM for smoother integration with frameworks such as Inferno and React Native.

The code actually needed for the state management is not very much, at least in my demos. It may need a little more work to be 100% generic for multiple VDOM APIs such as Ultradom, React, and React Native, hoping to do this over the weekend.

Interesting stuff @brodybits. But the part I like best about Hyperapp is the nested actions/state with wireSateToActions.

Exporting this one could help for dynamic import of actions 😜

So long as hyperapp remains just as easy to use, I'm good.

I don't really have a need to use hyperapp's state management with other view libraries.

@jorgebucaran you mind I try a PR just to see what it would look like? The idea is to have all current tests pass but have (at least) the state part decoupled and exported.

So long as hyperapp remains just as easy to use, I'm good.

it should be exactly the same, not even a breaking change.

Summary

Hyperapp will not use Ultradom Superfine as a dependency even though they both share the same virtual DOM implementation. Both projects will continue to be maintained separately. Components developed for Hyperapp will likely work with Superfine and viceversa, but this is not an official goal of either project.

While there are obvious advantages to splitting up the code into smaller dependencies, this time I'll keep everything in one place (two places, unfortunately). This will allow me to keep Hyperapp's core more focused, dependency-free and optimize for Hyperapp goals instead of sharing goals with a different project. I can also work faster this way.

Crosslinks

672

726

Was this page helpful?
0 / 5 - 0 ratings

Related issues

jorgebucaran picture jorgebucaran  ·  4Comments

dmitrykurmanov picture dmitrykurmanov  ·  4Comments

dmitrykurmanov picture dmitrykurmanov  ·  3Comments

joshuahiggins picture joshuahiggins  ·  4Comments

dwknippers picture dwknippers  ·  3Comments