Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Roadmap to V2 #696

Closed
7 of 10 tasks
jorgebucaran opened this issue May 24, 2018 · 42 comments
Closed
7 of 10 tasks

Roadmap to V2 #696

jorgebucaran opened this issue May 24, 2018 · 42 comments
Assignees
Labels
discussion meta News and communication outdated Forgotten lore

Comments

@jorgebucaran
Copy link
Owner

jorgebucaran commented May 24, 2018

As most of you may be well aware of, Hyperapp 2.0 is coming #672. 🎉

Here's my todo list before I am ready to publish its beta test version.


  • Split up code base into one file per every function (clone.js, patch.js, h.js, app.js, etc.)
  • Optimize defer function. Instead of setTimeout, use Promise.resolve (faster).
  • Implement new internals dispatch, setState, runCommand, render.
  • Update DOM manipulation functions (patch, createElement, updateAttribute) to accept resolveNode and eventProxy).
    • Implement new resolveNode to support children as a prop.
    • Determine how the new lazy components (dynamic) components will be implemented, e.g., passing dispatch as the second argument to every component or similar.
  • Implement the Subscriptions API.
    • [ ] Publish an independent library that demonstrates (or expands on) these ideas to start a discussion.
  • [ ] Implement targeted merges.
  • Add support to updateAttribute to compute a Element.className from an object, similar to classcat, but built-in.
  • Fix and add new tests.
  • Decide how the middleware API should work. /cc @okwolf

Your comments and feedback, please! 🙏

@jorgebucaran jorgebucaran added discussion meta News and communication breaking This will break things labels May 24, 2018
@jorgebucaran jorgebucaran added this to the 2.0 milestone May 24, 2018
@jorgebucaran jorgebucaran self-assigned this May 24, 2018
@jorgebucaran jorgebucaran changed the title Roadmap to 2.0 2.0 ToDo May 24, 2018
@jorgebucaran jorgebucaran changed the title 2.0 ToDo 📌2.0 ToDo May 24, 2018
@selfup
Copy link
Contributor

selfup commented May 24, 2018

Glad there is a thought out plan! Looking forward to the changes 🙏

@okwolf
Copy link
Contributor

okwolf commented May 24, 2018

Is there a task for middleware missing? Or will that be part of another task like dispatch?

@Swizz
Copy link
Contributor

Swizz commented May 24, 2018

The topic we were all awaiting 🎉

Is there some things we can help on ?

@jorgebucaran
Copy link
Owner Author

jorgebucaran commented May 24, 2018

@Swizz Tests 😅 and testing the beta release when it's published, also documentation ideas and the website, but notice I didn't include that on the todo list. Hyperapp 2.0 is coming first.

@SkaterDad
Copy link
Contributor

SkaterDad commented May 24, 2018

Great to see a todo list!

A few questions:

  1. If you're going to use Promise.resolve, what does this mean for your intended browser support?

    I'm sure most apps that support IE11 will already be bundling a Promise polyfill, but for those who weren't, this will need some documentation.

  2. What is the "targeted merges"? A utility function to manage updating nested state, or something like a vdom optimization?

  3. Will you be maintaining a 1.x branch and 2.x branch in parallel? I ask, because 1.x is already an extremely useful and nice framework, with some features that you seem to be dropping (state slices, "context-like" functionality of the lazy components).

    Could be an opportunity to make 2.x take advantage of modern language & platform features on modern browsers, and 1.x be maintained for older browsers and people who aren't interested in the managed effects system. Food for thought 🥘

@jorgebucaran
Copy link
Owner Author

  1. Resolve (pun intended) to setTimeout if typeof Promise === "undefined".
  2. A targeted merge is the best I could come up with the best equivalent of slices in 2.0.
  3. I haven't decided what to do about that. I think we could continue to support 1.x for a few months? and publish new patches or minors if there is a critical thing that needs to be updated.
    3.1. There may be lazy components, after all, I'm not too sure about it. How are you using its context-like functionality? I'd really like to hear that.

One way or another, 2.x is the way forward and how I plan to invest most of my Hyperapp time in the future.

@jorgebucaran
Copy link
Owner Author

jorgebucaran commented May 24, 2018

@SkaterDad Targeted/aimed merges (or targeted/aimed updates) work like this:

<button onclick={{
  foo: {
    bar: state => {
      // state === globalState.foo.bar

      return { justLikeSlices: true }

      // globalState = shallowMerge(
      //   immutablySet(globalState, "foo.bar", {
      //     justLikeSlices: true
      //   })
      // )
    }
  }
}} />

@Swizz
Copy link
Contributor

Swizz commented May 24, 2018

With a graceful 1.x to 2.x path, the adoption will be easier.

Hyperapp 1.x is not here since years, and I am pretty sure, Hyperapp 2.x will allow all this features to live in userland 👍

@SkaterDad
Copy link
Contributor

@jorgebucaran 3.1. There may be lazy components, after all, I'm not too sure about it. How are you using its context-like functionality? I'd really like to hear that.

My main app codebase was mostly in place by the time lazy components landed, so I'm not using them very much yet. There's one or two places where I used them to access state, but those components weren't deeply nested, so I mostly did it because I could. I'm sure some other people have used/abused it more than I have 😆

The biggest benefit my app had from lazy components was being able to access the global actions object, especially for my router's Link component. With 2.0's version of actions, this use case goes away.

@infinnie
Copy link
Contributor

infinnie commented May 25, 2018

Yeah access to global state and actions are somewhat like the new context API in React.

But maybe lazy components not only allow for things like a more elegant way of injecting dynamic actions without rewriting the actions, but could provide us with a more elegant alternative to things like Redux as well.


And maybe we could write components like

// This is only possible because Connector is a lazy component
// that knows the underlying state

// Maybe mutations should know the state whole, but I don’t know
var Component = function (props) {
    return (<Connector render={function (stateSlice, mutations, operations) {
        return (<Selector from={stateSlice} render={function (selectedState) {
            // Actual rendering goes here.
        }} />);
    }} />);
};

In the React community, there has been a recommended practice that

In general, the shape of the state is something only reducers should know about. The moment it leaks out of the store, code becomes structurally coupled.

Since reducers decide what’s the shape because they write it. It’s just common sense to make the “read” happen close to them with selectors.

As a rule of thumb, doing state. is a mistake. The only place where that should be allowed is inside a selector. But only as long as the selector is [collocated] in the reducer which is responsible for that part of the state.

https://hackernoon.com/selectors-in-redux-are-a-must-d6b0637c79b7

So revealing the state to userland is not a good practice. But for library authors, are lazy components a must? I am not so sure.


@infinnie I organized all your comments in the same post. I don't want this issue to go offtopic. Thanks.

@jorgebucaran Never mind & thanks. 🐈

Repository owner deleted a comment from infinnie May 25, 2018
Repository owner deleted a comment from infinnie May 25, 2018
@ThobyV
Copy link

ThobyV commented May 25, 2018

I'm so excited about this, I can't wait to start beta testing. I'd love to render some help though. 😎 (I'm probably good at writing tutorials).

@bardiarastin
Copy link

Hi, is there any reason code is not written in ES6 ?

@jorgebucaran
Copy link
Owner Author

@bardiarastin Other than ES modules, we're not using any modern JavaScript idioms.

The main reason is I want full control over the code I am writing and shipping and don't want to introduce a build step to the project. It's a modest attempt to make a case for simplicity.

@okwolf
Copy link
Contributor

okwolf commented May 30, 2018

👍 for a new subscription library

👍 for middleware API (#703)

@dcustodio
Copy link

First of all thanks for Hyperapp 1.x, it was just what I needed and all in a tiny lib.
I have a couple of projects running in 1.x that are good candidates to beta test the 2.0.

@rbiggs
Copy link
Contributor

rbiggs commented Jun 4, 2018

Curious if you'll refactor patch algo to improve Hyperapp performance in comparison to other frameworks on Stefan Krause's js-framework-benchmark.

@jorgebucaran
Copy link
Owner Author

@rbiggs Not for 2.0, but after (when I have time again). FWIW #663 improved those numbers substantially. You are welcome to discuss performance more in #499.

@emil14
Copy link

emil14 commented Jun 19, 2018

I am absolutely sure that you should use ESNext or maybe even TypeScript in hyperapp's sources.

Modern JavaScript gives you a better performance and much more simple code. Hyperapp's user already has Node.js and npm, probably used React and know something about Babel and Webpack. A lot of new features work in modern browsers out of the box and Hyperapp works on IE10+. There is no need to use ES5.

You, as a library developer, should teach developers to best practice. Build tools are good things and there is nothing bad if someone has to learn a bit more about ecosystem. Especially due to great Parcel which is very simple

@jorgebucaran
Copy link
Owner Author

jorgebucaran commented Jun 20, 2018

@emil14 Hyperapp doesn't need any modern JavaScript features at the moment, so performance is not affected because of this decision. For example, if we wanted to use Set or Map and because of poor browser support shipped our own shims, then you'd be totally right — we'd be better off using the browser's native Set and Map.

Things Hyperapp doesn't use or avoids without affecting its perf:

  • Classes, this, prototypes
  • Set, Map, WeakSet, WeakMap
  • Block-Scoped (let, const)
  • Destructuring
  • Proxies

@leeoniya
Copy link

FWIW, not using prototypes may in fact negatively affect performance. But you don't need any modern JS features or tooling to use them if you wanted to.

@jorgebucaran
Copy link
Owner Author

jorgebucaran commented Jun 20, 2018

@leeoniya

I am not saying I could've used prototypes, but decided not to because I have thisphobia. 😄

I am saying Hyperapp doesn't make use of prototypes. Hyperapps don't have methods or properties and there are no app instances either. In 1.0, even if you had multiple apps running on the same page (not encouraged either) they are unlikely to share the same implementation of actions, so you'd need to create a different actions object for every app call.

In 2.0 there is not even an actions object, so I've yet to find a use case for prototypes in Hyperapp.

@leeoniya
Copy link

i also keep this and new out of domvm's public apis. however, there are legitimate perf reasons for using prototypes internally.

@jorgebucaran
Copy link
Owner Author

Thanks, if you find a good reason to use it in Hyperapp let us know.

@emil14
Copy link

emil14 commented Jun 20, 2018

@jorgebucaran

Thank you for your answer! But I still don't agree with you :)

You can write sources in ESNext and let us to choose, should we transpile it or not, what browser support we do need. You can ship ES5 via CDN and add something like hyperapp.es5.js to the npm-package, don't you?

If you afraid of greater bundle size, generated by Webpack/Parcel/Rollup - do you really think it's so important ? :)

Classes, this, prototypes

I can understand that. These features are not very "functional", but the rest of them are good

@jorgebucaran
Copy link
Owner Author

You can write sources in ESNext and let us to choose, should we transpile it or not, what browser support we do need.

Nope, sorry, you are confused. Now, let me try to set the record straight.

Features like [].map, that were introduced in the ES5 revision, we use a few times.

Features like classes or arrow functions introduced in the ES6 revision we do not use anywhere. There is one important feature we do use, however, and that is ES modules.

https://github.com/hyperapp/hyperapp/blob/8da3edec673f9b67da03771b8713bda3a2b23519/src/index.js#L1
https://github.com/hyperapp/hyperapp/blob/8da3edec673f9b67da03771b8713bda3a2b23519/src/index.js#L29

So, what does this mean?

This means that you, in fact, need to transpile Hyperapp into actual ES3 or ES5 through Babel (or your compiler of choice) when creating a production bundle.

You can also import { h, app } from "hyperapp" using type=module, for example, see this CodePen.

Where is this file?

It's src/index.js. Here is some (mildly useless) trivia — because it's just single file, you don't even need unpkg or a CDN that can serve the raw ES modules code. The following actually works!

import { h, app } from "https://cdn.rawgit.com/hyperapp/hyperapp/8da3edec/src/index.js"

You can ship ES5 via CDN and add something like hyperapp.es5.js to the npm-package, don't you?

We do that!
https://github.com/hyperapp/hyperapp/blob/8da3edec673f9b67da03771b8713bda3a2b23519/package.json#L26-L29


If you afraid of greater bundle size, generated by Webpack/Parcel/Rollup - do you really think it's so important ? :)

We already use rollup to bundle Hyperapp into a UMD. But this is only useful for CDN users.

@infinnie
Copy link
Contributor

infinnie commented Jul 18, 2018

A few ES 6 features could have been useful like promises. 😉

@okwolf
Copy link
Contributor

okwolf commented Aug 14, 2018

I believe subscriptions are already implemented, but are you still planning to include classcat style support?

@jorgebucaran
Copy link
Owner Author

@okwolf Yes, I want to. What do you think? :)

@okwolf
Copy link
Contributor

okwolf commented Aug 14, 2018

@jorgebucaran I like the idea. How much bloat are we talking about adding?

@frenzzy
Copy link
Contributor

frenzzy commented Aug 14, 2018

Please no, it is not needed in case of using CSS Modules or CSS in JS.

@jorgebucaran
Copy link
Owner Author

jorgebucaran commented Aug 14, 2018

@okwolf Not a lot. See https://github.com/jorgebucaran/classcat

@frenzzy This feature is consistent and harmonious with VDOM and Hyperapp. But you make a point. How do you personally use CSS modules or CSS in JS with Hyperapp? Do you have any examples? Maybe that could help me make up my mind.

EDIT: I clarify. I am quite confident about this feature. I don't strongly believe we ought to have it, but I would not be unconfortable in the slightest if we did introduce it. Treating some attributes as objects is consistent with how V2 is shaping up, e.g., actions.

@zaceno
Copy link
Contributor

zaceno commented Aug 14, 2018

@frenzzy I don't understand. How does CSS modules or CSS in JS make classcat unnecessary?

With plain css I'd use it like this:

<ul>
  {items.map((text, index) => (
    <li class={cc({
      highlight: index === state.selected,
      disabled: state.disabled.indexOf(index) >= 0
    })}> {text} </li>
  )}
</ul>

with css moduled (with classes imported as the var style), like this:

<ul>
  {items.map((text, index) => (
    <li class={cc({
      [style.highlight]: index === state.selected,
      [style.disabled]: state.disabled.indexOf(index) >= 0
    })}> {text} </li>
  )}
</ul>

sure, the syntax is a little more verbose with css modules, but in both cases classcat (cc in the example) significantly simplifies the code for toggling and concatenating classes. And that's quite a common need. In fact I use it in all but my most trivial projects. So I'm pro including it.


Edit: To illustrate how classcat helps, this is one way I might do it without classcat (better options welcome):

<ul>
  {items.map((text, index) => (
    <li class={
      [
        state.selected=== index && style.highlight,
        state.disabled.indexOf(index) >=0 && style.disabled,
      ].filter(x => !!x).join(' ')
    }> {text} </li>
  )}
</ul>

... do that enough places and you start looking for a library to do it for you, or write your own.

@dmitry-kurmanov
Copy link

May be classcat (and probably other features) can be separate module because of flexibility? Everyone can include it if wants or not. Also I think that it can be exists in the official doc as the recommended way.

@zaceno
Copy link
Contributor

zaceno commented Aug 14, 2018

I think it's mostly a question of: how will this feature behave when you don't need to toggle classes. I think these should (and would) all work as expected with cc included:

class={'foo'}
class={'foo bar'}
class={['foo', 'bar']}
class={{foo: isFoo()}
class={['foo', {bar: isBar()}]}

...basically: writing regular class strings should still work. But the power of classcat is there built in when you need it. (Which, again, is quite often)

I would expect the "bloat" to be miniscule and well worth it

@jorgebucaran
Copy link
Owner Author

@zaceno Yep, as you said, they'd all work.

@thibautRe
Copy link

thibautRe commented Aug 14, 2018

How do you personally use CSS modules or CSS in JS with Hyperapp?

The way I use it right now is like this

// This helper generates a className and insert a style rule in the DOM.
// It can be for instance emotion-js, CSS modules "import", or a custom helper
const componentClass = generateClass('/* CSS rules here */')

const MyComponent = (props) => (
  <div class={componentClass} />
)

With conditional styles, I usually use ES6 string interpolation:

const componentBase = generateClass('/* CSS rules here */')
const isComponentActive = generateClass('')
const themes = {
  RED: generateClass('/* red theme */'),
  BLUE: generateClass('/* blue theme */'),
}

const MyComponent = ({ active, theme }) => (
  <div
    class={`
      ${componentBase}
      ${active && isComponentActive}
      ${themes[theme || 'RED']}
    `}
  />
)

// By default: inactive, default theme is red
<MyComponent />

// Activates the blue theme
<MyComponent active theme="BLUE" />

For my use cases, a classcat helper is not necessary.

@gpoitch
Copy link

gpoitch commented Aug 14, 2018

Usually use this simple inline class concat or can extract to a helper if used a lot:

import style from './style.css'

const FooComponent = ({ active }) => (
  <div class={[style.foo, active && style.active].filter(Boolean).join(' ')} />
)

@jorgebucaran
Copy link
Owner Author

jorgebucaran commented Aug 14, 2018

@thibautRe @gpoitch Thanks for sharing that.

Well, it seems neither CSS modules or CSS in JS would interfere with classcat-like built-in functionality.

@SkaterDad
Copy link
Contributor

Having classcat-like functionality built-in would be handy sometimes.

I pretty much exclusively use Tailwind.css now, with some sprinkles of custom CSS classes, so the CSS-in-JS stuff is a non-issue.

@mrozbarry
Copy link
Contributor

@jorgebucaran did we drop the idea of middleware? If not, maybe classcat could be vdom middleware that checks props for class, and if it's an object, parses, and if it's a string, passes that through as normal - that keeps it out of everyone's hair, but would make it easy enough to flip a switch and enable it.

@jorgebucaran
Copy link
Owner Author

jorgebucaran commented Aug 14, 2018

@mrozbarry did we drop the idea of middleware?

See #703.

@jorgebucaran jorgebucaran removed this from the V2 milestone Aug 31, 2018
@jorgebucaran jorgebucaran changed the title 📌2.0 ToDo V2 Todo Aug 31, 2018
@jorgebucaran
Copy link
Owner Author

jorgebucaran commented Aug 31, 2018

The V2 PR is here #726.

Repository owner locked and limited conversation to collaborators Aug 31, 2018
@jorgebucaran jorgebucaran added outdated Forgotten lore and removed breaking This will break things labels Aug 31, 2018
@jorgebucaran jorgebucaran changed the title V2 Todo Roadmap to V2 Sep 15, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
discussion meta News and communication outdated Forgotten lore
Projects
None yet
Development

No branches or pull requests