Add shallowCompare module and use it in PureRenderMixin + tests
cpojer committed
Mar 9, 2015 at 18:11 UTC
a1631bea7108c250ae7a284f8ada0eb2fa893b95
4 files changed
+176
-3
src/addons/ReactComponentWithPureRenderMixin.js
+2
-3
@@ -11,7 +11,7 @@
11
12
'use strict';
13
14
-var shallowEqual = require('shallowEqual');
14
+var shallowCompare = require('shallowCompare');
15
16
/**
17
* If your React component's render function is "pure", e.g. it will render the
@@ -39,8 +39,7 @@ var shallowEqual = require('shallowEqual');
39
*/
40
var ReactComponentWithPureRenderMixin = {
41
shouldComponentUpdate: function(nextProps, nextState) {
42
- return !shallowEqual(this.props, nextProps) ||
43
- !shallowEqual(this.state, nextState);
42
+ return shallowCompare(this, nextProps, nextState);
43
}
44
};
45
src/addons/__tests__/ReactComponentWithPureRenderMixin-test.js
new
+145
@@ -0,0 +1,145 @@
1
+/**
2
+ * Copyright 2015, Facebook, Inc.
3
+ * All rights reserved.
4
+ *
5
+ * This source code is licensed under the BSD-style license found in the
6
+ * LICENSE file in the root directory of this source tree. An additional grant
7
+ * of patent rights can be found in the PATENTS file in the same directory.
8
+ *
9
+ * @emails react-core
10
+ */
11
+
12
+'use strict';
13
+
14
+var React;
15
+var ReactComponentWithPureRenderMixin;
16
+var ReactTestUtils;
17
+
18
+describe('ReactComponentWithPureRenderMixin', function() {
19
+
20
+ beforeEach(function() {
21
+ React = require('React');
22
+ ReactComponentWithPureRenderMixin =
23
+ require('ReactComponentWithPureRenderMixin');
24
+ ReactTestUtils = require("../../ReactTestUtils");
25
+ });
26
+
27
+ it('provides a default shouldComponentUpdate implementation', function() {
28
+ var renderCalls = 0;
29
+ class PlasticWrap extends React.Component {
30
+ constructor(props, context) {
31
+ super(props, context);
32
+ this.state = {
33
+ color: 'green'
34
+ };
35
+ }
36
+
37
+ render() {
38
+ return (
39
+ <Apple
40
+ color={this.state.color}
41
+ ref="apple"
42
+ />
43
+ );
44
+ }
45
+ }
46
+
47
+ var Apple = React.createClass({
48
+ mixins: [ReactComponentWithPureRenderMixin],
49
+
50
+ getInitialState: function() {
51
+ return {
52
+ cut: false,
53
+ slices: 1,
54
+ }
55
+ },
56
+
57
+ cut: function() {
58
+ this.setState({
59
+ cut: true,
60
+ slices: 10,
61
+ });
62
+ },
63
+
64
+ eatSlice: function() {
65
+ this.setState({
66
+ slices: this.state.slices - 1,
67
+ });
68
+ },
69
+
70
+ render: function() {
71
+ renderCalls++;
72
+ return <div />;
73
+ }
74
+ });
75
+
76
+ var instance = ReactTestUtils.renderIntoDocument(<PlasticWrap />);
77
+ expect(renderCalls).toBe(1);
78
+
79
+ // Do not re-render based on props
80
+ instance.setState({color: 'green'});
81
+ expect(renderCalls).toBe(1);
82
+
83
+ // Re-render based on props
84
+ instance.setState({color: 'red'});
85
+ expect(renderCalls).toBe(2);
86
+
87
+ // Re-render base on state
88
+ instance.refs.apple.cut();
89
+ expect(renderCalls).toBe(3);
90
+
91
+ // No re-render based on state
92
+ instance.refs.apple.cut();
93
+ expect(renderCalls).toBe(3);
94
+
95
+ // Re-render based on state again
96
+ instance.refs.apple.eatSlice();
97
+ expect(renderCalls).toBe(4);
98
+ });
99
+
100
+ it('does not do a deep comparison', function() {
101
+ function getInitialState() {
102
+ return {
103
+ foo: [1, 2, 3],
104
+ bar: {a: 4, b: 5, c: 6},
105
+ };
106
+ }
107
+
108
+ var renderCalls = 0;
109
+ var initialSettings = getInitialState();
110
+
111
+ var Component = React.createClass({
112
+ mixins: [ReactComponentWithPureRenderMixin],
113
+
114
+ getInitialState: function() {
115
+ return initialSettings;
116
+ },
117
+
118
+ render: function() {
119
+ renderCalls++;
120
+ return <div />;
121
+ }
122
+ });
123
+
124
+ var instance = ReactTestUtils.renderIntoDocument(<Component />);
125
+ expect(renderCalls).toBe(1);
126
+
127
+ // Do not re-render if state is equal
128
+ var settings = {
129
+ foo: initialSettings.foo,
130
+ bar: initialSettings.bar,
131
+ };
132
+ instance.setState(settings);
133
+ expect(renderCalls).toBe(1);
134
+
135
+ // Re-render because one field changed
136
+ initialSettings.foo = [1, 2, 3];
137
+ instance.setState(initialSettings);
138
+ expect(renderCalls).toBe(2);
139
+
140
+ // Re-render because the object changed
141
+ instance.setState(getInitialState());
142
+ expect(renderCalls).toBe(3);
143
+ });
144
+
145
+});
src/addons/shallowCompare.js
new
+27
@@ -0,0 +1,27 @@
1
+/**
2
+ * Copyright 2013-2015, Facebook, Inc.
3
+ * All rights reserved.
4
+ *
5
+ * This source code is licensed under the BSD-style license found in the
6
+ * LICENSE file in the root directory of this source tree. An additional grant
7
+ * of patent rights can be found in the PATENTS file in the same directory.
8
+ *
9
+* @providesModule shallowCompare
10
+*/
11
+
12
+'use strict';
13
+
14
+var shallowEqual = require('shallowEqual');
15
+
16
+/**
17
+ * Does a shallow comparison for props and state.
18
+ * See ReactComponentWithPureRenderMixin
19
+ */
20
+function shallowCompare(instance, nextProps, nextState) {
21
+ return (
22
+ !shallowEqual(instance.props, nextProps) ||
23
+ !shallowEqual(instance.state, nextState)
24
+ );
25
+}
26
+
27
+module.exports = shallowCompare;
src/browser/ReactWithAddons.js
+2
@@ -29,6 +29,7 @@ var ReactUpdates = require('ReactUpdates');
29
30
var cx = require('cx');
31
var cloneWithProps = require('cloneWithProps');
32
+var shallowCompare = require('shallowCompare');
33
var update = require('update');
34
35
React.addons = {
@@ -41,6 +42,7 @@ React.addons = {
42
classSet: cx,
43
cloneWithProps: cloneWithProps,
44
createFragment: ReactFragment.create,
45
+ shallowCompare: shallowCompare,
46
update: update
47
};
48