Neatly Packaged Magic

    reaffect
    TypeScript icon, indicating that this package has built-in type declarations

    2.2.0 • Public • Published

    Reaffect

    npm size deps

    Reaffect is a reactive side-effect container for Javascript apps.

    It allows building apps where side-effects are a function of the current state of the application, and separate from business logic.

    Getting started

    API

    Examples

    Installation

    npm i reaffect

    Defining effects

    An effect is just a function which takes a dispatcher function as the first argument, and returns a canceller function.

    The dispatcher function takes a value as the first argument, and whether the effect is finished as the second argument.

    function SendEachSecond(dispatch, valueToSend) {
      const id = setInterval(() => dispatch(valueToSend, false), 1000)
      return () => clearInterval(id)
    }

    Effects can be made higher-order:

    const WithLog = (dispatch, f, ...args) => {
      const str = JSON.stringify([f.name, ...args])
      console.log(`${str} started`)
      const cancel = f((value, done) => {
        if (!done)
          console.log(`${str} sent "${value}"`)
        dispatch(value, done)
      }, ...args)
      return () => { cancel(); console.log(`${str} cancelled`) }
    }
    const WithTakeN = (dispatch, n, f, ...args) => {
      let k = 0
      return f((value, done) => dispatch(value, done || k++ < n), ...args)
    }
    const WithDiscard = (dispatch, f, ...args) =>
      f((value, done) => done && dispatch(null, done), ...args)

    Activating effects

    Activating effects and retrieving their sent values can be done inside a generator function or any object with a next method.

    import { reaffect } from 'reaffect'
     
    function* app() { 
      while (true) {
        const msg = yield [
          [SendEachSecond, 'hello'],
          [WithLog, SendEachSecond, 'world'],
        ]
        console.log(msg)
      }
    }
     
    reaffect(app())

    Generators are composable:

    function* screen1() { 
      while (true) {
        const msg = yield [/* ... */]
        if (msg === 'screen2')
          yield* screen2()
      }
    }

    And can be made higher-order:

    const withLogAll = gen => ({
      next(v) {
        v = gen.next(v)
        return { done: v.done, value: v.done || v.value.map(e => [WithLog, ...e]) }
      }
    })

    Examples

    React counter

    import React from 'react'
    import { render } from 'react-dom'
    import { reaffect } from 'reaffect'
     
    const SendEverySecond = (dispatch, value) =>
      clearInterval.bind(this, setInterval(dispatch, 1000, value))
     
    const Render = (dispatch, count) => {
      render(
        <div>
          <div>{count}</div>
          <button onClick={() => dispatch('increase')}>+1</button>
          <button onClick={() => dispatch('decrease')}>-1</button>
        </div>
      , document.getElementById('root')))
      return () => dispatch = () => {}
    }
     
    function* app() { 
      let count = 0
      while (true) {
        const msg = yield [
          [Render, count], 
          count > 0 && [SendEachSecond, 'decrease'],
          count < 0 && [SendEachSecond, 'increase'],
        ]
        switch (msg) {
          case 'increase':
            count++
            break
          case 'decrease':
            count--
            break
        }
      }
    }
     
    reaffect(app())

    Compatibility with Async Iterators

    Although async iterators are not really immediately cancellable, .return() will do it after the next promise resolves.

    const WrapAsyncIterator = (dispatch, f, ...args) => {
      const it = (async () => {
        for await (const v of f(...args))
          dispatch(v)
        dispatch(null, true)
      })()
      return () => it.return()
    }

    Acknowledgements

    This library is inspired by hyperapp and Elm.

    License

    MIT

    Install

    npm i reaffect

    DownloadsWeekly Downloads

    2

    Version

    2.2.0

    License

    MIT

    Unpacked Size

    7.97 kB

    Total Files

    5

    Last publish

    Collaborators

    • rliang