|  | 
| 93 | 93 | - [State](#state) | 
| 94 | 94 | - [Routing](#routing) | 
| 95 | 95 | - [Server Rendering](#server-rendering) | 
|  | 96 | +- [Components](#components) | 
| 96 | 97 | - [Optimizations](#optimizations) | 
| 97 | 98 | - [FAQ](#faq) | 
| 98 | 99 | - [API](#api) | 
| @@ -256,6 +257,13 @@ The current name of the route used in the router (e.g. `/foo/:bar`). | 
| 256 | 257 | ### `state.title` | 
| 257 | 258 | The current page title. Can be set using the `DOMTitleChange` event. | 
| 258 | 259 | 
 | 
|  | 260 | +### `state.components` | 
|  | 261 | +An object _recommended_ to use for local component state. | 
|  | 262 | + | 
|  | 263 | +### `state.cache(Component, id, [...args])` | 
|  | 264 | +Generic class cache. Will lookup Component instance by id and create one if not | 
|  | 265 | +found. Usefull for working with statefull [components](#components). | 
|  | 266 | + | 
| 259 | 267 | ## Routing | 
| 260 | 268 | Choo is an application level framework. This means that it takes care of | 
| 261 | 269 | everything related to routing and pathnames for you. | 
| @@ -340,6 +348,93 @@ the `window` object. | 
| 340 | 348 | </html> | 
| 341 | 349 | ``` | 
| 342 | 350 | 
 | 
|  | 351 | +## Components | 
|  | 352 | +From time to time there will arise a need to have an element in an application | 
|  | 353 | +hold a self-contained state or to not rerender when the application does. This | 
|  | 354 | +is common when using 3rd party libraries to e.g. display an interactive map or a | 
|  | 355 | +graph and you rely on this 3rd party library to handle modifications to the DOM. | 
|  | 356 | +Components come baked in to Choo for these kinds of situations. See | 
|  | 357 | +[nanocomponent][nanocomponent] for documentation on the component class. | 
|  | 358 | + | 
|  | 359 | +```javascript | 
|  | 360 | +// map.js | 
|  | 361 | +var html = require('choo/html') | 
|  | 362 | +var mapboxgl = require('mapbox-gl') | 
|  | 363 | +var Component = require('choo/component') | 
|  | 364 | + | 
|  | 365 | +module.exports = class Button extends Component { | 
|  | 366 | +  constructor (id, state, emit) { | 
|  | 367 | +    super(id) | 
|  | 368 | +    this.local = state.components[id] = {} | 
|  | 369 | +  } | 
|  | 370 | + | 
|  | 371 | +  load (element) { | 
|  | 372 | +    this.map = new mapboxgl.Map({ | 
|  | 373 | +      container: element, | 
|  | 374 | +      center: this.local.center | 
|  | 375 | +    }) | 
|  | 376 | +  } | 
|  | 377 | + | 
|  | 378 | +  update (center) { | 
|  | 379 | +    if (center.join() !== this.local.center.join()) { | 
|  | 380 | +      this.map.setCenter(center) | 
|  | 381 | +    } | 
|  | 382 | +    return false | 
|  | 383 | +  } | 
|  | 384 | + | 
|  | 385 | +  createElement (center) { | 
|  | 386 | +    this.local.center = center | 
|  | 387 | +    return html`<div></div>` | 
|  | 388 | +  } | 
|  | 389 | +} | 
|  | 390 | +``` | 
|  | 391 | + | 
|  | 392 | +```javascript | 
|  | 393 | +// index.js | 
|  | 394 | +var choo = require('choo') | 
|  | 395 | +var html = require('choo/html') | 
|  | 396 | +var Map = require('./map.js') | 
|  | 397 | + | 
|  | 398 | +var app = choo() | 
|  | 399 | +app.route('/', mainView) | 
|  | 400 | +app.mount('body') | 
|  | 401 | + | 
|  | 402 | +function mainView (state, emit) { | 
|  | 403 | +  return html` | 
|  | 404 | +    <body> | 
|  | 405 | +      <button onclick=${onclick}>Where am i?</button> | 
|  | 406 | +      ${state.cache(Map, 'my-map').render(state.center)} | 
|  | 407 | +    </body> | 
|  | 408 | +  ` | 
|  | 409 | + | 
|  | 410 | +  function onclick () { | 
|  | 411 | +    emit('locate') | 
|  | 412 | +  } | 
|  | 413 | +} | 
|  | 414 | + | 
|  | 415 | +app.use(function (state, emitter) { | 
|  | 416 | +  state.center = [18.0704503, 59.3244897] | 
|  | 417 | +  emitter.on('locate', function () { | 
|  | 418 | +    window.navigator.geolocation.getCurrentPosition(function (position) { | 
|  | 419 | +      state.center = [position.coords.longitude, position.coords.latitude] | 
|  | 420 | +      emitter.emit('render') | 
|  | 421 | +    }) | 
|  | 422 | +  }) | 
|  | 423 | +}) | 
|  | 424 | +``` | 
|  | 425 | + | 
|  | 426 | +### Caching components | 
|  | 427 | +When working with stateful components, one will need to keep track of component | 
|  | 428 | +instances – `state.cache` does just that. The component cache is a function | 
|  | 429 | +which takes a component class and a unique id (`string`) as it's first two | 
|  | 430 | +arguments. Any following arguments will be forwarded to the component contructor | 
|  | 431 | +together with `state` and `emit`. | 
|  | 432 | + | 
|  | 433 | +The default class cache is an LRU cache (using [nanolru][nanolru]), meaning it | 
|  | 434 | +will only hold on to a fixed amount of class instances (`100` by default) before | 
|  | 435 | +starting to evict the least-recently-used instances. This behavior can be | 
|  | 436 | +overriden with [options](#app--chooopts). | 
|  | 437 | + | 
| 343 | 438 | ## Optimizations | 
| 344 | 439 | Choo is reasonably fast out of the box. But sometimes you might hit a scenario | 
| 345 | 440 | where a particular part of the UI slows down the application, and you want to | 
| @@ -434,6 +529,9 @@ Initialize a new `choo` instance. `opts` can also contain the following values: | 
| 434 | 529 |   history API. | 
| 435 | 530 | - __opts.href:__ default: `true`. Handle all relative `<a | 
| 436 | 531 |   href="<location>"></a>` clicks and call `emit('render')` | 
|  | 532 | +- __opts.cache:__ default: `undefined`. Override default class cache used by | 
|  | 533 | +  `state.cache`. Can be a a `number` (maximum number of instances in cache, | 
|  | 534 | +  default `100`) or an `object` with a [nanolru][nanolru]-compatible API. | 
| 437 | 535 | 
 | 
| 438 | 536 | ### `app.use(callback(state, emitter, app))` | 
| 439 | 537 | Call a function and pass it a `state`, `emitter` and `app`. `emitter` is an instance | 
| @@ -583,6 +681,8 @@ Become a backer, and buy us a coffee (or perhaps lunch?) every month or so. | 
| 583 | 681 | ## License | 
| 584 | 682 | [MIT](https://tldrlegal.com/license/mit-license) | 
| 585 | 683 | 
 | 
|  | 684 | +[nanocomponent]: https://github.com/choojs/nanocomponent | 
|  | 685 | +[nanolru]: https://github.com/s3ththompson/nanolru | 
| 586 | 686 | [bankai]: https://github.com/choojs/bankai | 
| 587 | 687 | [bel]: https://github.com/shama/bel | 
| 588 | 688 | [nanohtml]: https://github.com/choojs/nanohtml | 
|  | 
0 commit comments