From 2b7dc1901caff29212551a4be99299cddc4e1cf5 Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Thu, 21 Jul 2016 21:59:01 +0200 Subject: [PATCH 1/3] .use: add new api method --- README.md | 78 ++++++++++++++++++++++++++++++++++++++++--------------- index.js | 17 ++++++++---- 2 files changed, 69 insertions(+), 26 deletions(-) diff --git a/README.md b/README.md index b832ee50..81f9cff9 100644 --- a/README.md +++ b/README.md @@ -382,7 +382,35 @@ const view = (state, prev, send) => { ` } ``` -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: @@ -400,26 +428,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 @@ -460,6 +470,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 diff --git a/index.js b/index.js index a047d754..e0206293 100644 --- a/index.js +++ b/index.js @@ -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 @@ -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) @@ -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) { From d3dda7d113b72001a311beffaa7f626610c9c233 Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Fri, 22 Jul 2016 00:08:12 +0200 Subject: [PATCH 2/3] docs: add big warning about plugins --- README.md | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 81f9cff9..eb6851ac 100644 --- a/README.md +++ b/README.md @@ -408,9 +408,15 @@ 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). +After all they're the most powerful way to modify a `choo` appliction. + +__:warning: Warning :warning:: plugins should only be used as a last resort. +It creates peer dependencies which makes upgrading versions and switching +frameworks a lot harder. Please exhaust all other options before using +plugins.__ + +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: @@ -485,6 +491,11 @@ There are several `hooks` that are picked up by `choo`: - __onStateChange(action, state, prev, caller, createSend):__ called after a reducer changes the `state`. +__:warning: Warning :warning:: plugins should only be used as a last resort. +It creates peer dependencies which makes upgrading versions and switching +frameworks a lot harder. Please exhaust all other options before using +plugins.__ + `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 From 4f0da3a739164639948cd3db160381eb924c8987 Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Sun, 24 Jul 2016 22:01:31 +0100 Subject: [PATCH 3/3] deps: add barracks deps --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 7e38caf9..cb13c2f6 100644 --- a/package.json +++ b/package.json @@ -36,7 +36,7 @@ ], "license": "MIT", "dependencies": { - "barracks": "^8.0.0", + "barracks": "^8.1.1", "document-ready": "~1.0.2", "global": "^4.3.0", "hash-match": "^1.0.2",