@samitouri / QOS-React-2 / commits / 61d3dd0e08

Update deepDiffer usage in React Native renderer (#17282)

* Add RN prop diffing test with function values * Update RN deepDiffer mock * Explicitly ignore functions in RN prop differ

Moti Zilberman committed Nov 7, 2019 at 04:00 UTC 61d3dd0e08a53eebeaf06537b84b78dedbc92168
4 files changed +83 -8
packages/react-native-renderer/src/ReactNativeAttributePayload.js
+1 -1
@@ -38,7 +38,7 @@ function defaultDiffer(prevProp: mixed, nextProp: mixed): boolean {
38 return true;
39 } else {
40 // For objects and arrays, the default diffing algorithm is a deep compare
41 - return deepDiffer(prevProp, nextProp);
41 + return deepDiffer(prevProp, nextProp, {unsafelyIgnoreFunctions: true});
42 }
43 }
44
packages/react-native-renderer/src/__mocks__/react-native/Libraries/ReactPrivate/deepDiffer.js
+29 -6
@@ -3,20 +3,43 @@
3 *
4 * This source code is licensed under the MIT license found in the
5 * LICENSE file in the root directory of this source tree.
6 + *
7 + * @format
8 + * @flow
9 */
10
11 'use strict';
12
10 -// TODO: Move deepDiffer into react
13 +type Options = {|+unsafelyIgnoreFunctions?: boolean|};
14
12 -const deepDiffer = function(one: any, two: any): boolean {
15 +/*
16 + * @returns {bool} true if different, false if equal
17 + */
18 +const deepDiffer = function(
19 + one: any,
20 + two: any,
21 + maxDepthOrOptions: Options | number = -1,
22 + maybeOptions?: Options,
23 +): boolean {
24 + const options =
25 + typeof maxDepthOrOptions === 'number' ? maybeOptions : maxDepthOrOptions;
26 + const maxDepth =
27 + typeof maxDepthOrOptions === 'number' ? maxDepthOrOptions : -1;
28 + if (maxDepth === 0) {
29 + return true;
30 + }
31 if (one === two) {
32 // Short circuit on identical object references instead of traversing them.
33 return false;
34 }
35 if (typeof one === 'function' && typeof two === 'function') {
18 - // We consider all functions equal
19 - return false;
36 + // We consider all functions equal unless explicitly configured otherwise
37 + let unsafelyIgnoreFunctions =
38 + options == null ? null : options.unsafelyIgnoreFunctions;
39 + if (unsafelyIgnoreFunctions == null) {
40 + unsafelyIgnoreFunctions = true;
41 + }
42 + return !unsafelyIgnoreFunctions;
43 }
44 if (typeof one !== 'object' || one === null) {
45 // Primitives can be directly compared
@@ -37,13 +60,13 @@ const deepDiffer = function(one: any, two: any): boolean {
60 return true;
61 }
62 for (let ii = 0; ii < len; ii++) {
40 - if (deepDiffer(one[ii], two[ii])) {
63 + if (deepDiffer(one[ii], two[ii], maxDepth - 1, options)) {
64 return true;
65 }
66 }
67 } else {
68 for (const key in one) {
46 - if (deepDiffer(one[key], two[key])) {
69 + if (deepDiffer(one[key], two[key], maxDepth - 1, options)) {
70 return true;
71 }
72 }
packages/react-native-renderer/src/__tests__/ReactNativeAttributePayload-test.js
+40
@@ -231,4 +231,44 @@ describe('ReactNativeAttributePayload', () => {
231 ),
232 ).toEqual({a: null, c: true});
233 });
234 +
235 + it('should skip changed functions', () => {
236 + expect(
237 + diff(
238 + {
239 + a: function() {
240 + return 1;
241 + },
242 + },
243 + {
244 + a: function() {
245 + return 9;
246 + },
247 + },
248 + {a: true},
249 + ),
250 + ).toEqual(null);
251 + });
252 +
253 + it('should skip deeply-nested changed functions', () => {
254 + expect(
255 + diff(
256 + {
257 + wrapper: {
258 + a: function() {
259 + return 1;
260 + },
261 + },
262 + },
263 + {
264 + wrapper: {
265 + a: function() {
266 + return 9;
267 + },
268 + },
269 + },
270 + {wrapper: true},
271 + ),
272 + ).toEqual(null);
273 + });
274 });
scripts/flow/react-native-host-hooks.js
+13 -1
@@ -19,8 +19,20 @@ import type {
19 import type {RNTopLevelEventType} from 'legacy-events/TopLevelEventTypes';
20 import type {CapturedError} from 'react-reconciler/src/ReactCapturedValue';
21
22 +type DeepDifferOptions = {|+unsafelyIgnoreFunctions?: boolean|};
23 +
24 declare module 'react-native/Libraries/ReactPrivate/ReactNativePrivateInterface' {
23 - declare export function deepDiffer(one: any, two: any): boolean;
25 + declare export function deepDiffer(
26 + one: any,
27 + two: any,
28 + maxDepth?: number,
29 + options?: DeepDifferOptions,
30 + ): boolean;
31 + declare export function deepDiffer(
32 + one: any,
33 + two: any,
34 + options: DeepDifferOptions,
35 + ): boolean;
36 declare export function deepFreezeAndThrowOnMutationInDev<T>(obj: T): T;
37 declare export function flattenStyle(style: any): any;
38 declare export var RCTEventEmitter: {