@samitouri / QOS-React-2 / commits / a1631bea71

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