@benev/frog
TypeScript icon, indicating that this package has built-in type declarations

0.6.0-dev.5 • Public • Published

🐸 frog – frontend web framework

🕹️ live demo: https://frog.benev.gg/
📦 frog is an npm package: @benev/frog
📜 documentation coming sooner or later..
❤️ frog is free and open source



🥞 Flatstate

flatstate helps you create state objects and reaction functions which are called when properties change.

flatstate is inspired by mobx and snapstate, but designed to be really simple: flatstate only works on flat state objects, only the direct properties of state objects are tracked for reactivity.

flatstate basics

  • create a flatstate tracking context
    import {Flat} from "@benev/frog"
    
    const flat = new Flat()
  • make a flat state object
    const state = flat.state({count: 0})
  • setup a reaction
    flat.reaction(() => console.log(state.count))
      //-> 0
    
    state.count++
      //-> 1
    • flatstate records which state properties your reaction reads
    • flatstate calls your reaction whenever those specific properties change
    • your reaction can listen to more than one state object

flatstate details

  • reactions are debounced -- so you may have to wait to see state changes
    const flat = new Flat()
    const state = flat.state({amount: 100})
    
    state.amount = 101
    console.log(state.amount) //-> 100 (old value)
    
    await flat.wait
    console.log(state.amount) //-> 101 (now it's ready)
  • you can stop a reaction
    const stop = flat.reaction(() => console.log(state.count))
    
    stop() // end this particular reaction
  • clear all reactions on a flatstate instance
    // clear all reactions on this flat instance
    flat.clear()

flatstate reactions

  • so first, there's a simple one-function reaction:
    flat.reaction(() => console.log(state.count))
    • flatstate immediately runs the function, and records which properties it reads
    • then, anytime one of those properties changes, it runs your function again
  • you can also do a two-function reaction:
    flat.reaction(
      () => ({count: state.count}),
      ({count}) => console.log(count),
    )
    • now there's a separation between your "collector" and your "responder"
    • the collector "passes" relevant data to the responder function
    • flatstate calls the responder whenever that data changes
  • there's also this helper called "collectivize" if you prefer the syntax sugar:
    const c = Flat.collectivize(state)
    
    flat.reaction(
      c(({count}) => ({count})),
      ({count}) => console.log(count)
    )
  • there's also something called "deepReaction"
    flat.deepReaction(() => console.log(state.count))
    • it's the same as "reaction", but it has "discovery" enabled
    • discovery means the collector is checked again for every responder call
    • it's less efficient, but allows you to respond to deeply nested recursive structures
  • there's also .auto and .manual reactions
    • these allow you to set options like discovery and debounce (you can turn off the debouncer)
    • but that's bigbrain stuff that you'll have to read the sourcecode about

flatstate advanced

  • multiple flatstate instances are totally isolated from each other
    const flat1 = new Flat()
    const flat2 = new Flat()
  • create readonly access to a state object
    const state = flat.state({count: 0})
    const rstate = Flat.readonly(state)
    
    state.count = 1
    await flat.wait
    console.log(rstate.count) //-> 1
    
    rstate.count = 2 // !! ReadonlyError !!
    • btw, you can use readonly on anything, not just flatstate

flatstate integration with frontend elements

  • let your components rerender on flat state changes
    import {flatstate_reactivity} from "@benev/frog"
    
    const elements = flatstate_reactivity(flat)(elements)


🪈 Pipe

  • pipe data through a series of functions
  • maybe you've done silly nesting like this:
    // bad
    register_to_dom(
      mixin_flatstate_reactivity(flat)(
        apply_theme(theme)(
          provide_context(context)(elements)
        )
      )
    )
  • now you can do this instead:
    import {Pipe} from "@benev/frog"
    
    // good
    Pipe.with(elements)
      .to(provide_context(context))
      .to(apply_theme(theme))
      .to(flatstate_reactivity(flat))
      .to(register_to_dom)


💫 Op

utility for ui loading/err/ready states.

useful for implementing async operations that involve loading indicators.

  • ops are just plain objects, and they have a mode string (loading/err/ready)
    import {Op} from "@benev/frog"
    
    console.log(Op.make.loading())
      //-> {mode: "loading"}
    
    console.log(Op.make.err("a fail occurred"))
      //-> {mode: "err", reason: "a fail occurred"}
    
    console.log(Op.make.ready(123))
      //-> {mode: "ready", payload: 123}
  • you can run an async operation that will update your op accordingly
    let my_op = Op.make.loading()
    
    await Op.run(op => my_op = op, async() => {
      await nap(1000)
      return 123
    })
  • functions to interrogate an op
      //        type for op in any mode
      //                 v
    function lol(op: Op.Any<number>) {
    
      // branching based on the op's mode
      Op.select(op, {
        loading: () => console.log("op is loading"),
        err: reason => console.log("op is err", reason),
        ready: payload => console.log("op is ready", payload)
      })
    
      const payload = Op.payload(op)
        // if the mode=ready, return the payload
        // otherwise, return undefined
    }

Package Sidebar

Install

npm i @benev/frog

Weekly Downloads

2

Version

0.6.0-dev.5

License

MIT

Unpacked Size

1.44 MB

Total Files

318

Last publish

Collaborators

  • chasemoskal