Skip to content

Commit

Permalink
.use: add new api method
Browse files Browse the repository at this point in the history
  • Loading branch information
yoshuawuyts committed Jul 21, 2016
1 parent e26a2af commit ecd50cd
Show file tree
Hide file tree
Showing 2 changed files with 69 additions and 26 deletions.
78 changes: 57 additions & 21 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -380,7 +380,35 @@ const view = (state, prev, send) => {
</div>`
}
```
In this example, when the `Add` button is clicked, the view will dispatch an `add` action that the model’s `add` reducer will receive. [As seen above](#models), the reducer will add an item to the state’s `todos` array. The state change will cause this view to be run again with the new state, and the resulting DOM tree will be used to [efficiently patch the DOM](#does-choo-use-a-virtual-dom).
In this example, when the `Add` button is clicked, the view will dispatch an
`add` action that the model’s `add` reducer will receive. [As seen
above](#models), the reducer will add an item to the state’s `todos` array. The
state change will cause this view to be run again with the new state, and the
resulting DOM tree will be used to [efficiently patch the
DOM](#does-choo-use-a-virtual-dom).

### Plugins
Sometimes it's necessary to change the way `choo` itself works. For example to
report whenever an action is triggered, handle errors globally or perist state
somewhere. This is done through something called `plugins`. Plugins are objects
that contain `hook` functions and are passed to `app.use()`:

```js
const log = require('choo-log')
const choo = require('choo')
const app = choo()

app.use(log())

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

Generally people using `choo` shouldn't be too worried about the specifics of
`plugins`, as the internal API is (unfortunatly by necessecity) quite complex.
After all they're the most powerful way to modify a `choo` appliction. If you
want to learn more about creating your own `plugins`, and which `hooks` are
available, head on over to [app.use()](#appusehooks).

## Badges
Using `choo` in a project? Show off which version you've used using a badge:
Expand All @@ -398,26 +426,8 @@ the first time, consider reading through the [handbook][handbook] or
[concepts](#concepts) first :sparkles:

### app = choo(opts)
Initialize a new `choo` app. Takes an optional object of handlers. Handlers can
be:
- __onError(err, state, createSend):__ called when an `effect` or
`subscription` emit an error. If no handler is passed, the default handler
will `throw` on each error.
- __onAction(action, state, name, caller, createSend):__ called when an
`action` is fired.
- __onStateChange(action, state, prev, caller, createSend):__ called after a
reducer changes the `state`.

`createSend()` is a special function that allows the creation of a new named
`send()` function. The first argument should be a string which is the name, the
second argument is a boolean `callOnError` which can be set to `true` to call
the `onError` hook istead of a provided callback. It then returns a
`send(actionName, data?)` function.

Handlers should be used with care, as they're the most powerful interface into
the state. For application level code it's generally recommended to delegate to
actions inside models using the `send()` call, and only shape the actions
inside the handlers.
Initialize a new `choo` app. Takes an optional object of handlers that is
passed to [app.use()](#appusehooks).

### app.model(obj)
Create a new model. Models modify data and perform IO. Takes the following
Expand Down Expand Up @@ -458,6 +468,32 @@ is URI partials and `send()` can be called to trigger actions. If
`defaultRoute` is passed in, that will be called if no paths match. If no
`defaultRoute` is specified it will throw instead.

### app.use(hooks)
Register an object of hooks on the application. This is useful to extend the
way `choo` works, adding custom behavior and listeners. Generally returning
objects of `hooks` is done by returning them from functions (which we call
`plugins` throughout the documentation).

There are several `hooks` that are picked up by `choo`:
- __onError(err, state, createSend):__ called when an `effect` or
`subscription` emit an error. If no handler is passed, the default handler
will `throw` on each error.
- __onAction(action, state, name, caller, createSend):__ called when an
`action` is fired.
- __onStateChange(action, state, prev, caller, createSend):__ called after a
reducer changes the `state`.

`createSend()` is a special function that allows the creation of a new named
`send()` function. The first argument should be a string which is the name, the
second argument is a boolean `callOnError` which can be set to `true` to call
the `onError` hook istead of a provided callback. It then returns a
`send(actionName, data?)` function.

Hooks should be used with care, as they're the most powerful interface into
the state. For application level code it's generally recommended to delegate to
actions inside models using the `send()` call, and only shape the actions
inside the hooks.

### html = app.toString(route, state?)
Render the application to a string of HTML. Useful for rendering on the server.
First argument is a path that's passed to the router. Second argument is an
Expand Down
17 changes: 12 additions & 5 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,17 +18,21 @@ module.exports = choo
function choo (opts) {
opts = opts || {}

const _store = start._store = barracks(xtend(opts, { onStateChange: render }))
const _store = start._store = barracks()
var _router = start._router = null
var _defaultRoute = null
var _rootNode = null
var _routes = null
var _frame = null

_store.use({ onStateChange: render })
_store.use(opts)

start.toString = toString
start.router = router
start.model = model
start.start = start
start.use = use

return start

Expand Down Expand Up @@ -83,10 +87,6 @@ function choo (opts) {
// update the DOM after every state mutation
// (obj, obj, obj, str, fn) -> null
function render (data, state, prev, name, createSend) {
if (opts.onStateChange) {
opts.onStateChange(data, state, prev, name, createSend)
}

if (!_frame) {
_frame = nanoraf(function (state, prev) {
const newTree = _router(state.location.pathname, state, prev)
Expand All @@ -109,6 +109,13 @@ function choo (opts) {
_store.model(model)
}

// register a plugin
// (obj) -> null
function use (hooks) {
assert.equal(typeof hooks, 'object', 'choo.use: hooks should be an object')
_store.use(hooks)
}

// create a new router with a custom `createRoute()` function
// (str?, obj, fn?) -> null
function createRouter (defaultRoute, routes, createSend) {
Expand Down

0 comments on commit ecd50cd

Please sign in to comment.