JSPM

  • Created
  • Published
  • Downloads 30444
  • Score
    100M100P100Q125727F
  • License MIT

A framework for creating solid web applications

Package Exports

  • choo
  • choo/http

This package does not declare an exports field, so the exports above have been automatically detected and optimized by JSPM instead. If any package subpath is missing, it is recommended to post an issue to the original package (choo) to support the "exports" field. If that is not possible, create a JSPM override to customize the exports field for this package.

Readme

choo stability

npm version build status test coverage downloads js-standard-style

πŸš‚πŸš‹πŸš‹πŸš‹πŸš‹πŸš‹ - The little framework that could.

A framework for creating sturdy web applications. Built on years of industry experience it distills the essence of functional architectures into a productive package.

Features

  • minimal size: weighing under 8kb, choo is a tiny little framework
  • single state: immutable single state helps reason about changes
  • small api: with only 5 methods, there's not a lot to learn
  • minimal tooling: built for the cutting edge browserify compiler
  • transparent side effects: using "effects" and "subscriptions" brings clarity to IO
  • omakase: composed out of a balanced selection of open source packages
  • idempotent: renders seemlessly in both Node and browsers
  • very cute: choo choo!

Demos

Usage

const choo = require('choo')

const app = choo()
app.model('title', {
  state: {
    title: 'my-demo-app'
  },
  reducers: {
    'update': (action, state) => ({ title: action.payload })
  },
  effects: {
    'update': (action, state, send) => (document.title = action.payload)
  }
})

const mainView = (params, state, send) => choo.view`
  <main class="app">
    <h1>${state.title}</h1>
    <label>Set the title</label>
    <input
      type="text"
      placeholder=${state.title}
      oninput=${(e) => send('title:update', { payload: e.target.value })}>
  </main>
`

app.router((route) => [
  route('/', mainView)
])

const tree = app.start()
document.body.appendChild(tree)

Concepts

  • user: πŸ™†
  • DOM: the Document Object Model is what is currently displayed in your browser
  • actions: a named event with optional properties attached. Used to call effects and reducers that have been registered in models
  • model: optionally namespaced object containing subscriptions, effects and reducers
  • subscriptions: read-only data sources that emit actions
  • effects: asynchronous functions that emit an action when done
  • reducers: synchronous functions that modify state
  • state: a single object that contains all the values used in your application
  • router: determines which view to render
  • views: take state and returns a new DOM tree that is rendered in the browser
 β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”     β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”
 β”‚    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”    β”‚     β”‚  User  β”‚
 β”œβ”€β”€β”€β”€β”‚  Subscriptions  β”‚    β”‚     β””β”€β”€β”€β”€β”€β”€β”€β”€β”˜
 β”‚    β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€    β”‚          β”‚
 └────│     Effects     │◀────          β–Ό
      β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€  Actions β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”
      β”‚    Reducers     │◀───┴─────│  DOM   β”‚
    Modelsβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜          β””β”€β”€β”€β”€β”€β”€β”€β”€β”˜
               β”‚                        β–²
             State                   DOMβ”‚tree
               β–Ό                        β”‚
          β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”               β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”
          β”‚ Router │─────State ───▢│ Views  β”‚
          β””β”€β”€β”€β”€β”€β”€β”€β”€β”˜               β””β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Effects

Side effects are done through effects declared in app.model(). Unlike reducers they cannot modify the state by returning objects, but get a callback passed hich is used to emit actions to handle results.

A typical effect flow looks like:

  1. An action is received
  2. An effect is triggered
  3. The effect performs an async call
  4. When the async call is done, either a success or error action is emitted
  5. A reducer catches the action and updates the state

HTTP

choo ships with a built-in http module that weighs only 2.4kb:

const http = require('choo/http')

// GET JSON
http.get('/my-endpoint', { json: true }, function (err, res, body) {
  if (err) throw err
  if (res.statusCode !== 200 || !body) throw new Error('something went wrong')
})

// POST JSON
const body = { foo: 'bar' }
http.post('/my-endpoint', { json: body }, function (err, res, body) {
  if (err) throw err
  if (res.statusCode !== 200 || !body) throw new Error('something went wrong')
})

// DELETE
http.del('/my-endpoint', function (err, res) {
  if (err) throw err
  if (res.statusCode !== 200) throw new Error('something went wrong')
})

Note that http only runs in the browser to prevent accidental requests when rendering in Node. For more details view the raynos/xhr documentation.

Subscriptions (wip)

Subscriptions are a way of receiving data from a source. For example when listening for events from a server using SSE or Websockets for a chat app, or when catching keyboard input for a videogame.

Server Sent Events (SSE)

[tbi] - help and suggestions welcome!

Keyboard

[tbi] - help and suggestions welcome!

Websockets

[tbi] - help and suggestions welcome!

API

app = choo()

Create a new choo app

app.model(name?, obj)

Create a new model. Models modify data and perform IO. Obj takes the following arguments:

  • state: object. Key value store of initial values
  • reducers: object. Syncronous functions that modify state. Each function has a signature of (action, state)
  • effects: object. Asyncronous functions that perform IO. Each function has a signature of (action, state, send) where send is a reference to app.send()

If a name string is passed as a first argument, reducers and signatures will be prefixed by the name. So if name is "user" and a reducer called "update" is registered, it would be accessed as 'user:update' in send().

choo.view`html`

Tagged template string HTML builder. See yo-yo for full documentation. Views should be passed to app.router()

app.router(params, state, send)

Creates a new router. See sheet-router for full documentation. Registered views have a signature of (params, state, send), where params is URI partials.

tree = app.start()

Start the application. Returns a DOM element that can be mounted using document.body.appendChild().

Packages used

Optimizing

To bring down file size, consider running the following browserify transforms:

  • unassertify - remove assert() statements which reduces file size. Use as a --global transform
  • varify - replace const with var statements. Use as a --global transform
  • uglifyify - minify your code using UglifyJS2. Use as a --global transform

Packages that work well together

  • tachyons - functional CSS for humans
  • sheetify - modular CSS bundler for browserify

FAQ

How does choo compare to X?

  • react: [tbi]
  • mithril: [tbi]
  • preact: [tbi]
  • angular2: [tbi]

Installation

$ npm install choo

License

MIT