diff --git a/src/addons/ReactComponentWithPureRenderMixin.js b/src/addons/ReactComponentWithPureRenderMixin.js
index fc367d7c685b2..05265e17c160a 100644
--- a/src/addons/ReactComponentWithPureRenderMixin.js
+++ b/src/addons/ReactComponentWithPureRenderMixin.js
@@ -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
@@ -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);
}
};
diff --git a/src/addons/__tests__/ReactComponentWithPureRenderMixin-test.js b/src/addons/__tests__/ReactComponentWithPureRenderMixin-test.js
new file mode 100644
index 0000000000000..c8db634d218f3
--- /dev/null
+++ b/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 (
+
+ );
+ }
+ }
+
+ 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
;
+ }
+ });
+
+ var instance = ReactTestUtils.renderIntoDocument();
+ 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 ;
+ }
+ });
+
+ var instance = ReactTestUtils.renderIntoDocument();
+ 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);
+ });
+
+});
diff --git a/src/addons/shallowCompare.js b/src/addons/shallowCompare.js
new file mode 100644
index 0000000000000..4f6b17cdc8839
--- /dev/null
+++ b/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;
diff --git a/src/browser/ReactWithAddons.js b/src/browser/ReactWithAddons.js
index 6dfe9da083aba..e91c591419d9b 100644
--- a/src/browser/ReactWithAddons.js
+++ b/src/browser/ReactWithAddons.js
@@ -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 = {
@@ -41,6 +42,7 @@ React.addons = {
classSet: cx,
cloneWithProps: cloneWithProps,
createFragment: ReactFragment.create,
+ shallowCompare: shallowCompare,
update: update
};