Hyperapp

repository·main·Indexed 12 days ago

https://github.com/jorgebucaran/hyperapp

An ultra-lightweight (approx. 1kB) Virtual DOM, state management, and UI framework for building purely functional, declarative hypertext applications. Version 2.0.22 features a unidirectional data flow and a collection of official packages including @hyperapp/dom, @hyperapp/svg, @hyperapp/html, @hyperapp/time, and @hyperapp/events.

Tokens
21.3K
Snippets
78
Records
95
Agent score
94%

What's inside Hyperapp

  1. What is an Action in Hyperapp

    main

    An action is a message used within your app that signals a valid way to change the state.

    Technically, an action is a deterministic function that produces no side-effects. It describes a transition from the current state to the next state and can optionally list effects to be run. Actions are dispatched by DOM events, effecters, or subscribers. When dispatched, actions implicitly receive the current state as their first argument.

    Naming Convention: It is recommended to use PascalCase for action names (e.g., AddArticle, IncrementBy) to distinguish them as messages intended for Hyperapp. Use verbs or verb-noun phrases. Verbs can be imperative (ToggleVisibility) or past tense (GotData) when signaling the end of an action-effect chain.

    // Action Signature (Conceptual):
    // Action : (State, Payload?) -> NextState
    //                        | [NextState, ...Effects]
    //                        | OtherAction
    //                        | [OtherAction, Payload?]
  2. What is a View in Hyperapp

    main

    A view is a declarative description of what should be rendered, typically influenced by the current state. In Hyperapp, a view is implemented as a pure function that accepts the current state and returns a virtual DOM node (VNode). When state transitions occur, your views are automatically updated to reflect the new state.

    Signature:

    View : (State) -> VNode
    const view = (state) => h("div", {}, text(state.message))
  3. Building views with h(), text(), and memo()

    main

    You use three primary functions to describe your views:

    1. h(): Describes HTML elements and wires up actions (events). It takes an element name, a properties object, and children.
    2. text(): Creates text nodes. Use this for simple text content.
    3. memo(): An optimization function used with other VNode-producing functions to prevent unnecessary re-renders.

    Example using h() and text():

    const view = (state) =>
      h(
        "button",
        {
          class: { "calling-acid-burn": state.beingFramed },
          onclick: FindThatDisk,
        },
        text("It's in that place where I put that thing that time.")
      )
    const view = (state) =>
      h(
        "button",
        {
          class: { "calling-acid-burn": state.beingFramed },
          onclick: FindThatDisk,
        },
        text("It's in that place where I put that thing that time.")
      )
  4. Optimize rendering with memo()

    main

    Hyperapp provides memo() to implement memoization, an optimization technique that stores the result of a calculation to avoid re-computing it.

    In Hyperapp, memoization prevents a component from re-rendering unless its specific props have changed. Because Hyperapp relies on immutability, it can safely check if props are referentially equal to determine if a re-render is necessary.

    When to use memo():

    • Use it for nodes that do not need to update frequently or at all.
    • Avoid using it for nodes that must update on every state change; the overhead of checking props will actually decrease performance.

    Example:

    const view = (state) => memo(scenicView, state.vacationSpot)
    const view = (state) => memo(scenicView, state.vacationSpot)
  5. What are Effects in Hyperapp

    main

    An effect is a representation used by actions to interact with external processes (e.g., HTTP requests, DOM focus, local storage, WebSockets). They allow you to handle impure asynchronous interactions in a safe, pure, and immutable way.

    An effect is conceptually a tuple consisting of an effecter and an optional **payload.

    Naming Convention: It is recommended to name effects using camelCase with an imperative verb or verb-noun phrase (e.g., log or saveAsPDF).

  6. Define and dispatch actions to transform state

    main

    An action is a function that describes a transformation of the state. It takes the current state as its first argument and must return a new state object of the same shape.

    To trigger an action, assign it to an event handler (like onclick) in your view. Hyperapp will dispatch the action, use it to transform the state, and then re-render the view.

    // An action definition
    const ToggleHighlight = state => ({ ...state, highlight: !state.highlight })
    
    // Dispatching the action in a view
    h("input", {
      type: "checkbox",
      checked: state.highlight,
      onclick: ToggleHighlight,
    })
    const ToggleHighlight = state => ({ ...state, highlight: !state.highlight })
    
    h("input", {
      type: "checkbox",
      checked: state.highlight,
      onclick: ToggleHighlight,
    })
  7. Perform conditional rendering in views

    main

    You can conditionally show or hide elements in your view using standard JavaScript logical operators like && or ternary operators condition ? A : B.

    state => h("main", {}, [
      // ... other elements
      state.bio && h("div", { class: "bio" }, text(state.bio)),
    ])
    state => h("main", {}, [
      state.bio && h("div", { class: "bio" }, text(state.bio)),
    ])
  8. How subscriptions work in Hyperapp

    main

    While effects are how an app affects the outside world, subscriptions are how an app reacts to the outside world (e.g., listening to DOM events).

    A subscriber is a function that:

    1. Receives dispatch and options as arguments.
    2. Sets up a listener (e.g., addEventListener).
    3. Must return a cleanup function that tells Hyperapp how to stop listening (e.g., removeEventListener).

    A subscription is a tuple in the format [subscriber, options].

    Subscriptions are defined in the subscriptions property of the app configuration. This property accepts a function that receives the current state and returns an array of active subscriptions. Hyperapp automatically starts or stops subscriptions based on whether they are included in the returned array as the state changes.

    Example of a keydown subscription:

    const keydownSubscriber = (dispatch, options) => {
      const handler = ev => {
        if (ev.key !== options.key) return
        dispatch(options.action)
      }
      addEventListener("keydown", handler)
      return () => removeEventListener("keydown", handler)
    }
    
    const onKeyDown = (key, action) => [keydownSubscriber, {key, action}]
    
    app({
      ...,
      subscriptions: state => [
        state.selected !== null &&
        state.selected > 0 &&
        onKeyDown("ArrowUp", SelectUp),
    
        state.selected !== null &&
        state.selected < (state.ids.length - 1) &&
        onKeyDown("ArrowDown", SelectDown),
      ],
    })
    const keydownSubscriber = (dispatch, options) => {
      const handler = ev => {
        if (ev.key !== options.key) return
        dispatch(options.action)
      }
      addEventListener("keydown", handler)
      return () => removeEventListener("keydown", handler)
    }
    
    const onKeyDown = (key, action) => [keydownSubscriber, {key, action}]
    
    app({
      ...,
      subscriptions: state => [
        state.selected !== null &&
        state.selected > 0 &&
        onKeyDown("ArrowUp", SelectUp),
    
        state.selected !== null &&
        state.selected < (state.ids.length - 1) &&
        onKeyDown("ArrowDown", SelectDown),
      ],
    })
  9. What are Subscriptions in Hyperapp

    main

    A subscription represents a dependency your application has on an external process (e.g., time, location changes, or custom DOM events).

    Subscriptions allow you to handle impure, asynchronous interactions with the outside world in a safe, pure, and immutable way. They automate resource management, such as adding and removing event listeners or closing connections, based on the application state.

  10. Understand the concept of State in Hyperapp

    main

    In Hyperapp, state is the unified set of data that your views, actions, and subscriptions all access.

    Unlike many other frameworks, Hyperapp does not enforce local state within components. Instead, all data is stored in a single, unified state tree. This means components have direct access to any part of the global state they require, but the developer is responsible for defining the structure of this state.

  11. How effecters work in Hyperapp

    main

    Hyperapp actions are pure functions meant to calculate a new state. They are not intended for running arbitrary side effects like API calls. To run side effects, an action must return a tuple in the format [newState, effecter].

    An effecter (or effect runner) is a function that Hyperapp calls during the dispatch process. Hyperapp automatically provides the dispatch function as the first argument to the effecter, allowing you to dispatch new actions once your side effect (like a fetch) completes.

    Example of an action with an effecter:

    const Select = (state, selected) => [
      {...state, selected},
      dispatch => {
        fetch("https://jsonplaceholder.typicode.com/users/" + state.ids[selected])
          .then(response => response.json())
          .then(data => dispatch(GotBio, data))
      }
    ]
    const Select = (state, selected) => [
      {...state, selected},
      dispatch => {
        fetch("https://jsonplaceholder.typicode.com/users/" + state.ids[selected])
          .then(response => response.json())
          .then(data => dispatch(GotBio, data))
      }
    ]