├── .gitignore ├── .travis.yml ├── HISTORY.md ├── index.js ├── package.json ├── test.js └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "4" 4 | script: 5 | - npm test 6 | cache: 7 | directories: 8 | - node_modules 9 | -------------------------------------------------------------------------------- /HISTORY.md: -------------------------------------------------------------------------------- 1 | ## [v1.3.0] 2 | > May 3, 2016 3 | 4 | - Update [object-assign][] to 4.1.0, making it more robust in buggy Chrome engines. 5 | 6 | [v1.3.0]: https://github.com/rstacruz/deku-memoize/compare/v1.2.0...v1.3.0 7 | 8 | ## [v1.2.0] 9 | > Jan 1, 2016 10 | 11 | - Fixed issue when using with components used multiple times. 12 | 13 | ## [v1.1.0] 14 | > Jan 1, 2016 15 | 16 | - Initial release. 17 | 18 | [v1.1.0]: https://github.com/rstacuz/deku-memoize/tree/v1.1.0 19 | [v1.2.0]: https://github.com/rstacruz/deku-memoize/compare/v1.1.0...v1.2.0 20 | [object-assign]: https://www.npmjs.com/package/object-assign 21 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | var assign = require('object-assign') 2 | 3 | module.exports = function dekuMemoize (component) { 4 | if (typeof component !== 'object' || typeof component.render !== 'function') { 5 | throw new Error('deku-memoize: expected a component') 6 | } 7 | 8 | if (!component.shouldUpdate) return component 9 | 10 | return assign({}, component, { 11 | shouldUpdate: undefined, 12 | render: memoize( 13 | component.render, 14 | component.shouldUpdate) 15 | }) 16 | } 17 | 18 | function memoize (func, shouldUpdate) { 19 | var calls = {} 20 | 21 | var memoize = function (model) { 22 | if (!calls[model.path]) calls[model.path] = {} 23 | var cache = calls[model.path] 24 | if (cache.last && !shouldUpdate(model, cache.last)) return cache.result 25 | cache.result = func.call(this, model) 26 | cache.last = model 27 | return cache.result 28 | } 29 | 30 | return memoize 31 | } 32 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "deku-memoize", 3 | "description": "Higher-order Deku component to allow components to have a shouldUpdate method", 4 | "version": "1.3.0", 5 | "author": "Rico Sta. Cruz ", 6 | "bugs": { 7 | "url": "https://github.com/rstacruz/deku-memoize/issues" 8 | }, 9 | "dependencies": { 10 | "object-assign": "4.1.0", 11 | "standard": "6.0.8" 12 | }, 13 | "devDependencies": { 14 | "deep-equal": "1.0.1", 15 | "standard": "5.4.1", 16 | "tape": "4.4.0", 17 | "tape-standard": "1.0.0" 18 | }, 19 | "homepage": "https://github.com/rstacruz/deku-memoize#readme", 20 | "keywords": [ 21 | "deku", 22 | "memoize", 23 | "render", 24 | "shouldUpdate" 25 | ], 26 | "license": "MIT", 27 | "main": "index.js", 28 | "repository": { 29 | "type": "git", 30 | "url": "git+https://github.com/rstacruz/deku-memoize.git" 31 | }, 32 | "scripts": { 33 | "test": "node test.js" 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /test.js: -------------------------------------------------------------------------------- 1 | var test = require('tape') 2 | var memoize = require('./index') 3 | var deepEqual = require('deep-equal') 4 | 5 | test('memoize', function (t) { 6 | var calls = 0 7 | var component = memoize({ 8 | render: function () { calls++ }, 9 | shouldUpdate: function () { return false } 10 | }) 11 | 12 | component.render({ props: 0, path: 1 }) 13 | t.equal(calls, 1, 'called at first') 14 | 15 | component.render({ props: 0, path: 1 }) 16 | t.equal(calls, 1, 'not called again') 17 | 18 | component.render({ props: 0, path: 2 }) 19 | t.equal(calls, 2, 'called when path is different') 20 | t.end() 21 | }) 22 | 23 | test('memoize with objects', function (t) { 24 | var calls = 0 25 | var component = memoize({ 26 | render: function () { calls++ }, 27 | shouldUpdate: negate(deepEqual) 28 | }) 29 | 30 | component.render({ props: { name: 'john' }, path: 1 }) 31 | t.equal(calls, 1, 'called at first') 32 | 33 | component.render({ props: { name: 'sue' }, path: 1 }) 34 | t.equal(calls, 2, 'called when props changed') 35 | 36 | component.render({ props: { name: 'sue' }, path: 1 }) 37 | t.equal(calls, 2, 'not called when props havent changed') 38 | 39 | component.render({ props: { name: 'sue' }, path: 2 }) 40 | t.equal(calls, 3, 'called when path is different') 41 | t.end() 42 | }) 43 | 44 | function negate (fn) { 45 | return function () { return !fn.apply(this, arguments) } 46 | } 47 | 48 | test('standard', require('tape-standard')()) 49 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # deku-memoize 2 | 3 | Decorator to allow components to have a `shouldUpdate` method. For use with [deku] v2 or [decca]. 4 | 5 | [![Status](https://travis-ci.org/rstacruz/deku-memoize.svg?branch=master)](https://travis-ci.org/rstacruz/deku-memoize "See test builds") 6 | 7 | ## Usage 8 | 9 | `dekuMemoize(component)` takes in a deku Component, and returns another Component (a [decorator]). It will use the component's `shouldUpdate` funtion to check if there are updates to be done. 10 | 11 | ```js 12 | import dekuMemoize from 'deku-memoize' 13 | 14 | function render ({ props }) { 15 | return 16 | } 17 | 18 | // `next` and `prev` are the parameters passed onto `render()`. 19 | function shouldUpdate (next, prev) { 20 | return next.props.label !== prev.props.label 21 | } 22 | 23 | module.exports = dekuMemoize({ render, shouldUpdate }) 24 | ``` 25 | 26 | ## shouldUpdate 27 | 28 | `shouldUpdate` takes 2 arguments; both are the first parameters passed onto `render()`. If it returns `true`, then a render will be triggered; otherwise, `render()` will not be called. 29 | 30 | `shouldUpdate` will not be called on the first render. 31 | 32 | If shouldUpdate is not given, it will always re-render no matter what (default deku behavior). 33 | 34 | [deku]: http://dekujs.github.io/deku/ 35 | [decca]: http://ricostacruz.com/decca 36 | [decorator]: https://en.wikipedia.org/wiki/Decorator_pattern 37 | 38 | ## Thanks 39 | 40 | **deku-memoize** © 2015+, Rico Sta. Cruz. Released under the [MIT] License.
41 | Authored and maintained by Rico Sta. Cruz with help from contributors ([list][contributors]). 42 | 43 | > [ricostacruz.com](http://ricostacruz.com)  ·  44 | > GitHub [@rstacruz](https://github.com/rstacruz)  ·  45 | > Twitter [@rstacruz](https://twitter.com/rstacruz) 46 | 47 | [MIT]: http://mit-license.org/ 48 | [contributors]: http://github.com/rstacruz/deku-memoize/contributors 49 | 50 | [![](https://img.shields.io/badge/%E2%9C%93-collaborative_etiquette-brightgreen.svg)](http://git.io/col) 51 | --------------------------------------------------------------------------------