├── .babelrc ├── .gitignore ├── .travis.yml ├── README.md ├── index.js ├── package.json └── test └── test.js /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "stage": 0 3 | } 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "0.10" 4 | - "0.12" 5 | 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Build Status](https://travis-ci.org/brigand/react-purerender.svg?branch=master)](https://travis-ci.org/brigand/react-purerender) 2 | 3 | It's just syntax sugar. It does a few sanity checks and then sticks a shouldComponentUpdate function on your class. 4 | 5 | ```js 6 | import PureRender from 'react-purerender'; 7 | 8 | @PureRender 9 | class Foo extends React.Component { 10 | // stuff 11 | } 12 | ``` 13 | 14 | Sanity checks: 15 | - checks you don't call it on a falsy value (undefined) 16 | - checks the thing you call it on doesn't have shouldComponentUpdate 17 | 18 | The latter differes from the mixin implementation, because merging the results of should component update 19 | functions is weird and confusing. If you need that, shouldComponentUpdate is exposed 20 | 21 | ```js 22 | class Foo extends React.Component { 23 | shouldComponentUpdate(){ 24 | return PureRender.shouldComponentUpdate.call(this, ...arguments) || magicGlobalThingWasChanged(); 25 | } 26 | } 27 | ``` 28 | 29 | Or react-mixin: 30 | 31 | ```js 32 | import {shouldComponentUpdate} from 'react-purerender'; 33 | import reactMixin from 'reactMixin'; 34 | 35 | @reactMixin.decorate({shouldComponentUpdate}) 36 | class Foo extends React.Component { 37 | shouldComponentUpdate(){ 38 | // what happens if I return false and PureRender returns true? 39 | // I dunno... try it I guess, hope there's a test 40 | } 41 | } 42 | ``` 43 | 44 | That's it really, file an issue if you notice an edge case. I wrote this because I need it 45 | for most of my components and I've switched to es6 classes. 46 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | function PureRender(component){ 2 | if (typeof component !== "function") { 3 | throw new TypeError('PureRender: called without a component as the first argument'); 4 | } 5 | 6 | if (component.prototype.shouldComponentUpdate) { 7 | throw new Error('PureRender: called on a component that already implements shouldComponentUpdate'); 8 | } 9 | 10 | // mutation 11 | component.prototype.shouldComponentUpdate = PureRender.shouldComponentUpdate; 12 | 13 | return component; 14 | } 15 | module.exports = PureRender; 16 | 17 | var hasOwn = Object.prototype.hasOwnProperty; 18 | function shallowEquals(a, b){ 19 | // primitives (usually undefined) 20 | if (a === b) { 21 | return true; 22 | } 23 | 24 | var aKeys = Object.keys(a); 25 | var bKeys = Object.keys(b); 26 | 27 | // prevents us from having to look at each key in b 28 | if (aKeys.length !== bKeys.length) { 29 | return false; 30 | } 31 | 32 | // TODO: loop for perf 33 | return aKeys.every(function(key){ 34 | return hasOwn.call(b, key) && a[key] === b[key]; 35 | }); 36 | } 37 | 38 | PureRender.shouldComponentUpdate = function(nextProps, nextState){ 39 | return !shallowEquals(this.props, nextProps) || !shallowEquals(this.state, nextState); 40 | }; 41 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-purerender", 3 | "version": "0.1.0", 4 | "description": "the purerender mixin in a decorator compatible way", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "babel-node test/*.js" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "https://github.com/brigand/react-purerender.git" 12 | }, 13 | "keywords": [ 14 | "react", 15 | "react-components", 16 | "purerender", 17 | "immutable", 18 | "shouldComponentUpdate" 19 | ], 20 | "author": "Frankie Bagnardi ", 21 | "license": "MIT", 22 | "bugs": { 23 | "url": "https://github.com/brigand/react-purerender/issues" 24 | }, 25 | "homepage": "https://github.com/brigand/react-purerender", 26 | "devDependencies": { 27 | "babel": "^5.1.11", 28 | "react": "^0.13.2", 29 | "tape": "^4.0.0" 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /test/test.js: -------------------------------------------------------------------------------- 1 | import PureRender from '../'; 2 | var test = require('tape'); 3 | 4 | test('basics', function(t){ 5 | t.plan(4); 6 | 7 | @PureRender 8 | class Foo { 9 | constructor(){ 10 | this.props = {a: 1}; 11 | this.state = {b: 2}; 12 | } 13 | } 14 | 15 | var foo = new Foo(); 16 | t.ok(foo.shouldComponentUpdate, 'it exists'); 17 | t.equal(foo.shouldComponentUpdate({a: 1}, {b: 2}), false); 18 | t.equal(foo.shouldComponentUpdate({a: 2}, {b: 2}), true); 19 | t.equal(foo.shouldComponentUpdate({a: 1, c: 3}, {b: 2}), true); 20 | }); 21 | 22 | --------------------------------------------------------------------------------