1111
1212'use strict' ;
1313
14+ var PropTypes ;
1415var React ;
1516var ReactNative ;
1617var createReactNativeComponentClass ;
@@ -20,6 +21,7 @@ describe('ReactNative', () => {
2021 beforeEach ( ( ) => {
2122 jest . resetModules ( ) ;
2223
24+ PropTypes = require ( 'prop-types' ) ;
2325 React = require ( 'react' ) ;
2426 ReactNative = require ( 'react-native' ) ;
2527 UIManager = require ( 'UIManager' ) ;
@@ -60,6 +62,79 @@ describe('ReactNative', () => {
6062 expect ( UIManager . updateView ) . toBeCalledWith ( 2 , 'View' , { foo : 'bar' } ) ;
6163 } ) ;
6264
65+ it ( 'should not call UIManager.updateView after render for properties that have not changed' , ( ) => {
66+ const Text = createReactNativeComponentClass ( {
67+ validAttributes : { foo : true } ,
68+ uiViewClassName : 'Text' ,
69+ } ) ;
70+
71+ // Context hack is required for RN text rendering in stack.
72+ // TODO Remove this from the test when RN stack has been deleted.
73+ class Hack extends React . Component {
74+ static childContextTypes = { isInAParentText : PropTypes . bool } ;
75+ getChildContext ( ) {
76+ return { isInAParentText : true } ;
77+ }
78+ render ( ) {
79+ return this . props . children ;
80+ }
81+ }
82+
83+ ReactNative . render ( < Hack > < Text foo = "a" > 1</ Text > </ Hack > , 11 ) ;
84+ expect ( UIManager . updateView ) . not . toBeCalled ( ) ;
85+
86+ // If no properties have changed, we shouldn't call updateView.
87+ ReactNative . render ( < Hack > < Text foo = "a" > 1</ Text > </ Hack > , 11 ) ;
88+ expect ( UIManager . updateView ) . not . toBeCalled ( ) ;
89+
90+ // Only call updateView for the changed property (and not for text).
91+ ReactNative . render ( < Hack > < Text foo = "b" > 1</ Text > </ Hack > , 11 ) ;
92+ expect ( UIManager . updateView . mock . calls . length ) . toBe ( 1 ) ;
93+
94+ // Only call updateView for the changed text (and no other properties).
95+ ReactNative . render ( < Hack > < Text foo = "b" > 2</ Text > </ Hack > , 11 ) ;
96+ expect ( UIManager . updateView . mock . calls . length ) . toBe ( 2 ) ;
97+
98+ // Call updateView for both changed text and properties.
99+ ReactNative . render ( < Hack > < Text foo = "c" > 3</ Text > </ Hack > , 11 ) ;
100+ expect ( UIManager . updateView . mock . calls . length ) . toBe ( 4 ) ;
101+ } ) ;
102+
103+ it ( 'should not call UIManager.updateView from setNativeProps for properties that have not changed' , ( ) => {
104+ const View = createReactNativeComponentClass ( {
105+ validAttributes : { foo : true } ,
106+ uiViewClassName : 'View' ,
107+ } ) ;
108+
109+ class Subclass extends ReactNative . NativeComponent {
110+ render ( ) {
111+ return < View /> ;
112+ }
113+ }
114+
115+ [ View , Subclass ] . forEach ( Component => {
116+ UIManager . updateView . mockReset ( ) ;
117+
118+ let viewRef ;
119+ ReactNative . render (
120+ < Component
121+ foo = "bar"
122+ ref = { ref => {
123+ viewRef = ref ;
124+ } }
125+ /> ,
126+ 11 ,
127+ ) ;
128+ expect ( UIManager . updateView ) . not . toBeCalled ( ) ;
129+
130+ viewRef . setNativeProps ( { } ) ;
131+ expect ( UIManager . updateView ) . not . toBeCalled ( ) ;
132+
133+ viewRef . setNativeProps ( { foo : 'baz' } ) ;
134+ expect ( UIManager . updateView . mock . calls . length ) . toBe ( 1 ) ;
135+ } ) ;
136+ } ) ;
137+
63138 it ( 'returns the correct instance and calls it in the callback' , ( ) => {
64139 var View = createReactNativeComponentClass ( {
65140 validAttributes : { foo : true } ,
0 commit comments