Files
buuh/README.md
T
2016-05-16 02:26:43 +07:00

8.7 KiB

choo stability

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

:steam_locomotive::train::train::train::train:🚋 - 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
  • 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!

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     │◀─────┼─────│  DOM   │◀──────────────┐
     │ └─────────────────┘ │ Actions  └────────┘    DOM tree   │
       ┌─────────────────┐      │                              │
     │ │    Reducers     │◀┼────┘                              │
       └─────────────────┘                                     │
     │          │          │          ┌────────┐          ┌────────┐
                └────────────────────▶│ Router │─────────▶│ Views  │
     └ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┘  State   └────────┘  State   └────────┘
              Model

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

Installation

$ npm install choo

License

MIT