Hyperapp basics

In notebook:
Article Notes
Created at:
2018-08-26
Updated:
2018-08-31
Tags:
JavaScript libraries

Also, Effects API explained in GitHub: https://github.com/hyperapp/hyperapp/issues/750

jorge Bucaran's notes on Slack on hyperapp works

Originally posted in #1to2, I feel it's important knowledge I want @everyone subscribed to our Slack to know. Please read this. Take your time. :pray:

•••

What is Hyperapp's value?

Hyperapp offers us a declarative layer to create feature-rich, user interfaces (apps) and interact with the Web Platform.

In V1 we were limited to creating our views and handling DOM events using this declarative paradigm. If you need to make an http request, create a timer or listen to global/window events like onresize or onkeydown, you are on your own using the platform's, low-level, imperative API.

In V2 we gain "Effects" and "Subscriptions", which extend V1's declarative model and allow us to interact with the Web Platform using the same architecture. :tada:

Remember These Three

There are three categories of things Hyperapp apps will be able to interact with.

  • DOM EVENTS (HTML/SVG element events: onclick, ondblclick, oninput, onchange, onsubmit, onmousedown, ...) [V1/V2]

  • EFFECTS (async tasks/side effects, http requests, random numbers, delays, local storage, databases, accessing the camera/microphone, File API, WebVR API, ...) [V2]

  • SUBSCRIPTIONS (GlobalEvents, WebSockets, PWAs, Fullscreen API, Online State API, Geolocation API, Geofencing API, SPA (Navigation/Routing) popstate, hashchange, etc., setInterval, requestAnimationFrame, and any other Web API that relies in add/remove EventListeners, including your own custom APIs using CustomEvents, ...) [V2]

Show me examples!

API symmetry is super important to me. That's why V2's API will be consistent across these 3 categories mentioned above. This means, we interact with all of them in the same way.

ACTIONS

Calling a simple action.

<button onClick={DownloadMovies}>DOWNLOAD</button>

Calling an action passing some extra data.

const SelectMovie = (state, { movie }) => newState

// ...

state.movies(movie => (
  <section>
    <MovieInfo {...movie} />
    <button onClick={{ action: SelectMovie, props: { movie } }} />
  </section>
))

The same as above, but using an action-creator.

const SelectMovieHandler = (action, movie) => ({ action, props: { movie } })

// ...

state.movies(movie => (
  <section>
    <MovieInfo {...movie} />
    <button onClick={SelectMovieHandler(SelectMovie, movie)} />
  </section>
))

// Or using JSX for the action! It's up to you!

<button onClick={<SelectMovieHandler action={SelectMovie} movie={movie} />} />

EFFECTS

Random effect.

import * as Random from "@hyperapp/random"

export const Roll = state => [state, <Random.number min={1} max={10} action={NewFace} />]

Http fetch effect.

import * as Http from "@hyperapp/http"

export const PopulatePage = state => [
  state,
  <Http.fetch
    url="https://api.github.com/orgs/hyperapp/repos?per_page=100"
    action={UpdateRepoList}
  />
]

Notice that Roll and PopulatePage are regular actions and work just like the ones described in ACTIONS above. The only difference is that they return a array/tuple that contains:

[newState, theEffect]

This tells Hyperapp to first update the state with the new state and then start the async effect. When the effect is done, Hyperapp will dispatch the UpdateRepoList action.

const UpdateRepoList = (state, repos) => ({
  ...state,
  isFetching: false,
  repos: repos.map(repo => ({
    name: repo.name,
    url: repo.html_url,
    stars: repo.stargazers_count
  }))
})

SUBSCRIPTIONS

Same how we subscribe to a <button>'s onclick or <input> 's oninput, in V2 we'll be able to subscribe to global/external events.

app({
  //...,
  subscribe: state => [
    !state.paused && <Tick interval={1000} action={Elapse} />
  ]
})

Working with traditional event emitters requires a lot of complicated resource management like adding and removing event listeners, closing connections, clearing intervals — not to mention testing asynchronous code is hard. What happens when the source you are subscribed to shuts down? How do you cancel or restart a subscription?

Think of Subscriptions as VDOM not for elements, but for event streams! Events are represented using plain objects called subscriptions. When your program state changes we compare the new and old subscriptions, compute their differences and rewire the underlying connections to match the desired state.

The subscribe function can be one subscribe or an array of subscriptions.

export const subscriptions = state => [
  state.isIntro || state.isLost || state.isWon
    ? MouseClicks(startGame)
    : state.isInPlay && [
        AnimationFrame(tick),
        KeyDowns(keyPressed),
        KeyUps(move)
      ]
]

If a new subscription appears in the array, we'll start the subscription. When a subscription leaves the array, we'll cancel it. If any of its properties change, we'll restart it. Otherwise, we'll skip to the next subscription until we've seen them all. (edited)

Be my guest! Create a thread under this message, ask in #1to2 or #general, DM or open an issue on GitHub! I'd love to elaborate.

Notes: Do I need to use JSX to write effects, actions or subscriptions? Nope! JSX is just a function call. I use it because I personally like it, but you don't have to do it that way.

For example:


// is equivalent to

Time.tick({ interval: 1000, action: Roll })```

Cheers! :wave: (edited)