Git Product home page Git Product logo

hyperapp-effects's Introduction

Hyperapp Effects

Build Status Codecov npm

A Hyperapp Higher-Order App giving your app superpowers to write your effects as data, inspired by Elm Commands.

Installation

Node.js

Install with npm / Yarn.

npm i hyperapp-effects

Then with a module bundler like rollup or webpack, use as you would anything else.

import { withEffects } from "hyperapp-effects"

Or using require.

const { withEffects } = require("hyperapp-effects")

Browser

Download the minified library from the CDN.

<script src="https://unpkg.com/hyperapp-effects"></script>

You can find the library in window.effects.

API

Effects data

EffectTuple = [ string, object ]
Effect = EffectTuple | EffectTuple[] | Effect[]

Effects are always represented as arrays. For a single effect this array represents a tuple containing the effect type string and an object containing the properties of this effect. For multiple effects each array element is either an effect tuple or an array of these tuples, which may be nested. This means that effects are composeable.

withEffects

withEffects = function(App): App

This Higher-Order App function enables actions to return arrays which later will be run as effects.

Example:

import { withEffects } from "hyperapp-effects"

const state = {
  // ...
}

const actions = {
  foo: () => [
    // ... effects go here
  ],
  bar: () => // or a single effect can go here
}

withEffects(app)(state, actions).foo()

action

action = function(name: string, data?: any): EffectTuple

Describes an effect that will fire another action, optionally with data.

Example:

import { withEffects, action } from "hyperapp-effects"

const state = {
  // ...
}

const actions = {
  foo: () => [
    action("bar", { message: "hello" }),
    action("baz", { message: "hola" }),
    // ... other effects
  ],
  bar: data => {
    // data will have { message: "hello" }
  },
  baz: data => {
    // data will have { message: "hola" }
  }
}

withEffects(app)(state, actions).foo()

Note that you may also use a single action effect without an array wrapper and that nested actions may be called by separating the slices with dots:

import { withEffects, action } from "hyperapp-effects"

const state = {
  // ...
}

const actions = {
  foo: () => action("bar.baz", { message: "hello" }),
  bar: {
    baz: data => {
      // data will have { message: "hello" }
    }
  }
}

withEffects(app)(state, actions).foo()

This same convention follows for all the other effects as well.

Also note that action (and other effects) may be used for handler props in your view:

import { withEffects, action } from "hyperapp-effects"

const state = {
  // ...
}

const actions = {
  foo: data => {
    // data will have { message: "hello" }
    // when the button is clicked
  }
}

const view = () => h("button", {
  onclick: action("foo", { message: "hello" })
})

withEffects(app)(state, actions, view, document.body)

frame

frame = function(action: string): EffectTuple

Describes an effect that will call an action from inside requestAnimationFrame, which is also where the render triggered by the action will run. A relative timestamp will be provided as the action data. If you wish to have an action that continuously updates the state and rerenders inside of requestAnimationFrame (such as for a game), remember to include another frame effect in your return.

Example:

import { withEffects, action, frame } from "hyperapp-effects"

const state = {
  time: 0,
  delta: 0
}

const actions = {
  init: () => frame("update"),
  update: time => [
    action("incTime", time),

    // ...
    // Other actions to update the state based on delta time
    // ...

    // End with a recursive frame effect to perform the next update
    frame("update")
  ],
  incTime: time => ({ time: lastTime, delta: lastDelta }) => ({
    time,
    delta: time && lastTime ? time - lastTime : lastDelta
  })
}

withEffects(app)(state, actions).init()

delay

delay = function(duration: number, action: string, data?: any): EffectTuple

Describes an effect that will call an action after a delay using setTimeout, optionally with data.

Example:

import { withEffects, delay } from "hyperapp-effects"

const state = {
  // ...
}

const actions = {
  startTimer: () => delay(
    60000,
    "alarm",
    { name: "minute timer" }
  ),
  alarm: data => {
    // This action will run after a minute delay
    // data will have { name: "minute timer" }
  }
}

withEffects(app)(state, actions).startTimer()

time

time = function(action: string): EffectTuple

Describes an effect that will provide the current timestamp to an action using performance.now. The timestamp will be provided as the action data.

Example:

import { withEffects, time } from "hyperapp-effects"

const state = {
  // ...
}

const actions = {
  foo: () => time("bar"),
  bar: timestamp => {
    // use timestamp
  }
}

withEffects(app)(state, action).foo()

log

log = function(...args: any[]): EffectTuple

Describes an effect that will call console.log with arguments. Useful for development and debugging. Not recommended for production.

Example:

import { withEffects, log } from "hyperapp-effects"

const state = {
  // ...
}

const actions = {
  foo: () => log(
    "string arg",
    { object: "arg" },
    ["list", "of", "args"],
    someOtherArg
  )
}

withEffects(app)(state, actions).foo()

http

http = function(url: string, action: string, options?: object): EffectTuple

Describes an effect that will send an HTTP request using fetch and then call an action with the response. If you are using a browser from the Proterozoic Eon like Internet Explorer you will want a fetch polyfill. An optional options parameter supports the same options as fetch plus an additional response property specifying which method to use on the response body, defaulting to "json".

Example HTTP GET request with a JSON response:

import { withEffects, http } from "hyperapp-effects"

const state = {
  // ...
}

const actions = {
  foo: () => http("/data", "dataFetched"),
  dataFetched: data => {
    // data will have the JSON-decoded response from /data
  }
}

withEffects(app)(state, actions).foo()

Example HTTP GET request with a text response:

import { withEffects, http } from "hyperapp-effects"

const state = {
  // ...
}

const actions = {
  foo: () => http(
    "/data/name",
    "textFetched",
    { response: "text" }
  ),
  textFetched: data => {
    // data will have the response text from /data
  }
}

withEffects(app)(state, actions).foo()

Example HTTP POST request using JSON body and response:

import { withEffects, http } from "hyperapp-effects"

const state = {
  // ...
}

const actions = {
  login: form => http(
    "/login",
    "loginComplete",
    {
      method: "POST",
      body: form
    }
  ),
  loginComplete: () => loginResponse => {
    // loginResponse will have the JSON-decoded response from POSTing to /login
  }
}

withEffects(app)(state, actions).login()

event

event = function(action: string): EffectTuple

Describes an effect that will capture DOM Event data when attached to a handler in your view. The originally fired event will be provided as the action data.

import { withEffects, event } from "hyperapp-effects"

const state = {
  // ...
}

const actions = {
  click: clickEvent => {
    // clickEvent has the props of the click event
  }
}

const view = () => h("button", {
  onclick: event("click")
})

withEffects(app)(state, actions, view, document.body)

keydown

keydown = function(action: string): EffectTuple

Describes an effect that will capture keydown events for your entire document. The KeyboardEvent will be provided as the action data.

Example:

import { withEffects, keydown } from "hyperapp-effects"

const state = {
  // ...
}

const actions = {
  init: () => keydown("keyPressed"),
  keyPressed: keyEvent => {
    // keyEvent has the props of the KeyboardEvent
  }
}

withEffects(app)(state, actions).init()

keyup

keyup = function(action: string): EffectTuple

Describes an effect that will capture keyup events for your entire document. The KeyboardEvent will be provided as the action data.

Example:

import { withEffects, keyup } from "hyperapp-effects"

const state = {
  // ...
}

const actions = {
  init: () => keyup("keyReleased"),
  keyReleased: keyEvent => {
    // keyEvent has the props of the KeyboardEvent
  }
}

withEffects(app)(state, actions).init()

random

random = function(action: string, min?: number, max?: number): EffectTuple

Describes an effect that will call an action with a randomly generated number within a range. If provided the range will be [min, max) or else the default range is [0, 1). The random number will be provided as the action data.

Use Math.floor if you want a random integer instead of a floating-point number. Remember the range will be max exclusive, so use your largest desired int + 1.

Example:

import { withEffects, random } from "hyperapp-effects"

const state = {
  // ...
}

const actions = {
  // We use the max of 7 to include all values of 6.x
  foo: () => random("rollDie", 1, 7),
  rollDie: randomNumber => {
    const roll = Math.floor(randomNumber)
    // roll will be an int from 1-6
  }
}

withEffects(app)(state, actions).foo()

effectsIf

EffectConditional = [boolean, EffectTuple]
effectsIf = function(EffectConditional[]): EffectTuple[]

Convert an array of [boolean, EffectTuple]s into a new array of effects where the boolean evaluated to true. This provides compact syntatic sugar for conditionally firing effects.

Example:

import { withEffects, effectsIf, action } from "hyperapp-effects"

const state = {
  // ...
}

const actions = {
  foo: () => ({ running }) => effectsIf([
    [true, action("always")],
    [false, action("never")],
    [running, action("ifRunning")],
    [!running, action("ifNotRunning")]
  ])
}

withEffects(app)(state, actions).foo()

License

Hyperapp Effects is MIT licensed. See LICENSE.

hyperapp-effects's People

Contributors

forsakenharmony avatar okwolf avatar

Watchers

 avatar  avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.