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: {