Skip to content

Commit

Permalink
Merge pull request #3355 from cpojer/shallowCompare
Browse files Browse the repository at this point in the history
Add shallowCompare module and use it in PureRenderMixin + tests
  • Loading branch information
sebmarkbage committed Mar 13, 2015
2 parents d196362 + a1631be commit ca8d7cb
Show file tree
Hide file tree
Showing 4 changed files with 176 additions and 3 deletions.
5 changes: 2 additions & 3 deletions src/addons/ReactComponentWithPureRenderMixin.js
Expand Up @@ -11,7 +11,7 @@

'use strict';

var shallowEqual = require('shallowEqual');
var shallowCompare = require('shallowCompare');

/**
* If your React component's render function is "pure", e.g. it will render the
Expand Down Expand Up @@ -39,8 +39,7 @@ var shallowEqual = require('shallowEqual');
*/
var ReactComponentWithPureRenderMixin = {
shouldComponentUpdate: function(nextProps, nextState) {
return !shallowEqual(this.props, nextProps) ||
!shallowEqual(this.state, nextState);
return shallowCompare(this, nextProps, nextState);
}
};

Expand Down
145 changes: 145 additions & 0 deletions src/addons/__tests__/ReactComponentWithPureRenderMixin-test.js
@@ -0,0 +1,145 @@
/**
* Copyright 2015, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @emails react-core
*/

'use strict';

var React;
var ReactComponentWithPureRenderMixin;
var ReactTestUtils;

describe('ReactComponentWithPureRenderMixin', function() {

beforeEach(function() {
React = require('React');
ReactComponentWithPureRenderMixin =
require('ReactComponentWithPureRenderMixin');
ReactTestUtils = require("../../ReactTestUtils");
});

it('provides a default shouldComponentUpdate implementation', function() {
var renderCalls = 0;
class PlasticWrap extends React.Component {
constructor(props, context) {
super(props, context);
this.state = {
color: 'green'
};
}

render() {
return (
<Apple
color={this.state.color}
ref="apple"
/>
);
}
}

var Apple = React.createClass({
mixins: [ReactComponentWithPureRenderMixin],

getInitialState: function() {
return {
cut: false,
slices: 1,
}
},

cut: function() {
this.setState({
cut: true,
slices: 10,
});
},

eatSlice: function() {
this.setState({
slices: this.state.slices - 1,
});
},

render: function() {
renderCalls++;
return <div />;
}
});

var instance = ReactTestUtils.renderIntoDocument(<PlasticWrap />);
expect(renderCalls).toBe(1);

// Do not re-render based on props
instance.setState({color: 'green'});
expect(renderCalls).toBe(1);

// Re-render based on props
instance.setState({color: 'red'});
expect(renderCalls).toBe(2);

// Re-render base on state
instance.refs.apple.cut();
expect(renderCalls).toBe(3);

// No re-render based on state
instance.refs.apple.cut();
expect(renderCalls).toBe(3);

// Re-render based on state again
instance.refs.apple.eatSlice();
expect(renderCalls).toBe(4);
});

it('does not do a deep comparison', function() {
function getInitialState() {
return {
foo: [1, 2, 3],
bar: {a: 4, b: 5, c: 6},
};
}

var renderCalls = 0;
var initialSettings = getInitialState();

var Component = React.createClass({
mixins: [ReactComponentWithPureRenderMixin],

getInitialState: function() {
return initialSettings;
},

render: function() {
renderCalls++;
return <div />;
}
});

var instance = ReactTestUtils.renderIntoDocument(<Component />);
expect(renderCalls).toBe(1);

// Do not re-render if state is equal
var settings = {
foo: initialSettings.foo,
bar: initialSettings.bar,
};
instance.setState(settings);
expect(renderCalls).toBe(1);

// Re-render because one field changed
initialSettings.foo = [1, 2, 3];
instance.setState(initialSettings);
expect(renderCalls).toBe(2);

// Re-render because the object changed
instance.setState(getInitialState());
expect(renderCalls).toBe(3);
});

});
27 changes: 27 additions & 0 deletions src/addons/shallowCompare.js
@@ -0,0 +1,27 @@
/**
* Copyright 2013-2015, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @providesModule shallowCompare
*/

'use strict';

var shallowEqual = require('shallowEqual');

/**
* Does a shallow comparison for props and state.
* See ReactComponentWithPureRenderMixin
*/
function shallowCompare(instance, nextProps, nextState) {
return (
!shallowEqual(instance.props, nextProps) ||
!shallowEqual(instance.state, nextState)
);
}

module.exports = shallowCompare;
2 changes: 2 additions & 0 deletions src/browser/ReactWithAddons.js
Expand Up @@ -29,6 +29,7 @@ var ReactUpdates = require('ReactUpdates');

var cx = require('cx');
var cloneWithProps = require('cloneWithProps');
var shallowCompare = require('shallowCompare');
var update = require('update');

React.addons = {
Expand All @@ -41,6 +42,7 @@ React.addons = {
classSet: cx,
cloneWithProps: cloneWithProps,
createFragment: ReactFragment.create,
shallowCompare: shallowCompare,
update: update
};

Expand Down

0 comments on commit ca8d7cb

Please sign in to comment.