Skip to content

Commit 478db10

Browse files
feat: switch lodash.isEqual to fast-equals.deepEqual to improve performance (#4438)
* switch lodash.isEqual to fast-equals.deepEqual to improve performance * Revert "switch lodash.isEqual to fast-equals.deepEqual to improve performance" This reverts commit 86570fe. * use precompiledValidator @rjsf/utils.deepEquals * switch lodash.isEqual to @rjsf/utils.deepEquals at utils package * fix changelog and remove unnecessary comment
1 parent d211a7d commit 478db10

File tree

10 files changed

+43
-37
lines changed

10 files changed

+43
-37
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,12 @@ should change the heading of the (upcoming) version to include a major version b
2929
- Fixed issue with assigning default values to formData with deeply nested required properties, fixing [#4399](https://github.com/rjsf-team/react-jsonschema-form/issues/4399)
3030
- Fixed issue error message will not be cleared after the controlled Form formData is changed. Fixes [#4426](https://github.com/rjsf-team/react-jsonschema-form/issues/4426)
3131
- Fix for AJV [$data](https://ajv.js.org/guide/combining-schemas.html#data-reference) reference in const property in schema treated as default/const value. The issue is mentioned in [#4361](https://github.com/rjsf-team/react-jsonschema-form/issues/4361).
32+
- Switched uses of `lodash.isEqual()` to `@rjsf/utils.deepEquals`.
3233

3334
## @rjsf/validator-ajv8
3435

3536
- Partially fixed issue where dependency errors do not show `title` or `ui:title`. This fix only applicable if we use an ajv-i18n localizer. Ref. [#4402](https://github.com/rjsf-team/react-jsonschema-form/issues/4402).
37+
- Switched uses of `lodash.isEqual()` to `@rjsf/utils.deepEquals` at precompiledValidator.
3638

3739
# 5.23.2
3840

packages/utils/src/enumOptionsDeselectValue.ts

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
1-
import isEqual from 'lodash/isEqual';
2-
31
import { EnumOptionsType, RJSFSchema, StrictRJSFSchema } from './types';
42
import enumOptionsValueForIndex from './enumOptionsValueForIndex';
3+
import deepEquals from './deepEquals';
54

65
/** Removes the enum option value at the `valueIndex` from the currently `selected` (list of) value(s). If `selected` is
76
* a list, then that list is updated to remove the enum option value with the `valueIndex` in `allEnumOptions`. If it is
@@ -22,7 +21,7 @@ export default function enumOptionsDeselectValue<S extends StrictRJSFSchema = RJ
2221
): EnumOptionsType<S>['value'] | EnumOptionsType<S>['value'][] | undefined {
2322
const value = enumOptionsValueForIndex<S>(valueIndex, allEnumOptions);
2423
if (Array.isArray(selected)) {
25-
return selected.filter((v) => !isEqual(v, value));
24+
return selected.filter((v) => !deepEquals(v, value));
2625
}
27-
return isEqual(value, selected) ? undefined : selected;
26+
return deepEquals(value, selected) ? undefined : selected;
2827
}

packages/utils/src/enumOptionsIsSelected.ts

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
1-
import isEqual from 'lodash/isEqual';
2-
31
import { EnumOptionsType, RJSFSchema, StrictRJSFSchema } from './types';
2+
import deepEquals from './deepEquals';
43

54
/** Determines whether the given `value` is (one of) the `selected` value(s).
65
*
@@ -13,7 +12,7 @@ export default function enumOptionsIsSelected<S extends StrictRJSFSchema = RJSFS
1312
selected: EnumOptionsType<S>['value'] | EnumOptionsType<S>['value'][]
1413
) {
1514
if (Array.isArray(selected)) {
16-
return selected.some((sel) => isEqual(sel, value));
15+
return selected.some((sel) => deepEquals(sel, value));
1716
}
18-
return isEqual(selected, value);
17+
return deepEquals(selected, value);
1918
}

packages/utils/src/parser/ParserValidator.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import get from 'lodash/get';
2-
import isEqual from 'lodash/isEqual';
32

43
import { ID_KEY } from '../constants';
54
import hashForSchema from '../hashForSchema';
@@ -15,6 +14,7 @@ import {
1514
ValidationData,
1615
ValidatorType,
1716
} from '../types';
17+
import deepEquals from '../deepEquals';
1818

1919
/** The type of the map of schema hash to schema
2020
*/
@@ -67,7 +67,7 @@ export default class ParserValidator<T = any, S extends StrictRJSFSchema = RJSFS
6767
const existing = this.schemaMap[key];
6868
if (!existing) {
6969
this.schemaMap[key] = identifiedSchema;
70-
} else if (!isEqual(existing, identifiedSchema)) {
70+
} else if (!deepEquals(existing, identifiedSchema)) {
7171
console.error('existing schema:', JSON.stringify(existing, null, 2));
7272
console.error('new schema:', JSON.stringify(identifiedSchema, null, 2));
7373
throw new Error(
@@ -91,7 +91,7 @@ export default class ParserValidator<T = any, S extends StrictRJSFSchema = RJSFS
9191
* @throws - Error when the given `rootSchema` differs from the root schema provided during construction
9292
*/
9393
isValid(schema: S, _formData: T, rootSchema: S): boolean {
94-
if (!isEqual(rootSchema, this.rootSchema)) {
94+
if (!deepEquals(rootSchema, this.rootSchema)) {
9595
throw new Error('Unexpectedly calling isValid() with a rootSchema that differs from the construction rootSchema');
9696
}
9797
this.addSchema(schema, hashForSchema<S>(schema));

packages/utils/src/parser/schemaParser.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
import forEach from 'lodash/forEach';
2-
import isEqual from 'lodash/isEqual';
32

43
import { FormContextType, RJSFSchema, StrictRJSFSchema } from '../types';
5-
import { PROPERTIES_KEY, ITEMS_KEY } from '../constants';
4+
import { ITEMS_KEY, PROPERTIES_KEY } from '../constants';
65
import ParserValidator, { SchemaMap } from './ParserValidator';
7-
import { retrieveSchemaInternal, resolveAnyOrOneOfSchemas } from '../schema/retrieveSchema';
6+
import { resolveAnyOrOneOfSchemas, retrieveSchemaInternal } from '../schema/retrieveSchema';
7+
import deepEquals from '../deepEquals';
88

99
/** Recursive function used to parse the given `schema` belonging to the `rootSchema`. The `validator` is used to
1010
* capture the sub-schemas that the `isValid()` function is called with. For each schema returned by the
@@ -24,7 +24,7 @@ function parseSchema<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends
2424
) {
2525
const schemas = retrieveSchemaInternal<T, S, F>(validator, schema, rootSchema, undefined, true);
2626
schemas.forEach((schema) => {
27-
const sameSchemaIndex = recurseList.findIndex((item) => isEqual(item, schema));
27+
const sameSchemaIndex = recurseList.findIndex((item) => deepEquals(item, schema));
2828
if (sameSchemaIndex === -1) {
2929
recurseList.push(schema);
3030
const allOptions = resolveAnyOrOneOfSchemas<T, S, F>(validator, schema, rootSchema, true);

packages/utils/src/schema/getDefaultFormState.ts

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,16 @@
11
import get from 'lodash/get';
22
import isEmpty from 'lodash/isEmpty';
3+
import { JSONSchema7Object } from 'json-schema';
34

45
import {
6+
ALL_OF_KEY,
57
ANY_OF_KEY,
68
CONST_KEY,
79
DEFAULT_KEY,
810
DEPENDENCIES_KEY,
9-
PROPERTIES_KEY,
1011
ONE_OF_KEY,
12+
PROPERTIES_KEY,
1113
REF_KEY,
12-
ALL_OF_KEY,
1314
} from '../constants';
1415
import findSchemaDefinition from '../findSchemaDefinition';
1516
import getClosestMatchingOption from './getClosestMatchingOption';
@@ -33,10 +34,9 @@ import isMultiSelect from './isMultiSelect';
3334
import isSelect from './isSelect';
3435
import retrieveSchema, { resolveDependencies } from './retrieveSchema';
3536
import isConstant from '../isConstant';
36-
import { JSONSchema7Object } from 'json-schema';
3737
import constIsAjvDataReference from '../constIsAjvDataReference';
38-
import isEqual from 'lodash/isEqual';
3938
import optionsList from '../optionsList';
39+
import deepEquals from '../deepEquals';
4040

4141
const PRIMITIVE_TYPES = ['string', 'number', 'integer', 'boolean', 'null'];
4242

@@ -130,8 +130,7 @@ function maybeAddDefaultToObject<T = any>(
130130
if (!isEmpty(computedDefault)) {
131131
obj[key] = computedDefault;
132132
}
133-
}
134-
// Else store computedDefault if it's a non-empty object(e.g. not {}) and satisfies certain conditions
133+
} // Else store computedDefault if it's a non-empty object(e.g. not {}) and satisfies certain conditions
135134
// Condition 1: If computedDefault is not empty or if the key is a required field
136135
// Condition 2: If the parent object is required or emptyObjectFields is not 'populateRequiredDefaults'
137136
else if (
@@ -276,7 +275,10 @@ export function computeDefaults<T = any, S extends StrictRJSFSchema = RJSFSchema
276275
experimental_dfsb_to_compute?.constAsDefaults === 'skipOneOf'
277276
) {
278277
// If we are in a oneOf of a primitive type, then we want to pass constAsDefaults as 'never' for the recursion
279-
experimental_dfsb_to_compute = { ...experimental_dfsb_to_compute, constAsDefaults: 'never' };
278+
experimental_dfsb_to_compute = {
279+
...experimental_dfsb_to_compute,
280+
constAsDefaults: 'never',
281+
};
280282
}
281283
schemaToCompute = oneOf![
282284
getClosestMatchingOption<T, S, F>(
@@ -382,7 +384,7 @@ export function ensureFormDataMatchingSchema<
382384
let validFormData: T | T[] | undefined = formData;
383385
if (isSelectField) {
384386
const getOptionsList = optionsList(schema);
385-
const isValid = getOptionsList?.some((option) => isEqual(option.value, formData));
387+
const isValid = getOptionsList?.some((option) => deepEquals(option.value, formData));
386388
validFormData = isValid ? formData : undefined;
387389
}
388390

packages/utils/src/schema/retrieveSchema.ts

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import get from 'lodash/get';
2-
import isEqual from 'lodash/isEqual';
32
import set from 'lodash/set';
43
import times from 'lodash/times';
54
import transform from 'lodash/transform';
@@ -15,10 +14,10 @@ import {
1514
ANY_OF_KEY,
1615
DEPENDENCIES_KEY,
1716
IF_KEY,
17+
ITEMS_KEY,
1818
ONE_OF_KEY,
19-
REF_KEY,
2019
PROPERTIES_KEY,
21-
ITEMS_KEY,
20+
REF_KEY,
2221
} from '../constants';
2322
import findSchemaDefinition, { splitKeyElementFromObject } from '../findSchemaDefinition';
2423
import getDiscriminatorFieldFromSchema from '../getDiscriminatorFieldFromSchema';
@@ -34,6 +33,7 @@ import {
3433
ValidatorType,
3534
} from '../types';
3635
import getFirstMatchingOption from './getFirstMatchingOption';
36+
import deepEquals from '../deepEquals';
3737

3838
/** Retrieves an expanded schema that has had all of its conditions, additional properties, references and dependencies
3939
* resolved and merged into the `schema` given a `validator`, `rootSchema` and `rawFormData` that is used to do the
@@ -256,7 +256,10 @@ export function resolveSchema<T = any, S extends StrictRJSFSchema = RJSFSchema,
256256
)
257257
);
258258
const allPermutations = getAllPermutationsOfXxxOf<S>(allOfSchemaElements);
259-
return allPermutations.map((permutation) => ({ ...schema, allOf: permutation }));
259+
return allPermutations.map((permutation) => ({
260+
...schema,
261+
allOf: permutation,
262+
}));
260263
}
261264
// No $ref or dependencies or allOf attribute was found, returning the original schema.
262265
return [schema];
@@ -356,7 +359,7 @@ export function resolveAllReferences<S extends StrictRJSFSchema = RJSFSchema>(
356359
};
357360
}
358361

359-
return isEqual(schema, resolvedSchema) ? schema : resolvedSchema;
362+
return deepEquals(schema, resolvedSchema) ? schema : resolvedSchema;
360363
}
361364

362365
/** Creates new 'properties' items for each key in the `formData`

packages/utils/src/schema/toIdSchema.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import get from 'lodash/get';
2-
import isEqual from 'lodash/isEqual';
32

43
import { ALL_OF_KEY, DEPENDENCIES_KEY, ID_KEY, ITEMS_KEY, PROPERTIES_KEY, REF_KEY } from '../constants';
54
import isObject from '../isObject';
@@ -14,6 +13,7 @@ import {
1413
} from '../types';
1514
import retrieveSchema from './retrieveSchema';
1615
import getSchemaType from '../getSchemaType';
16+
import deepEquals from '../deepEquals';
1717

1818
/** An internal helper that generates an `IdSchema` object for the `schema`, recursively with protection against
1919
* infinite recursion
@@ -42,7 +42,7 @@ function toIdSchemaInternal<T = any, S extends StrictRJSFSchema = RJSFSchema, F
4242
): IdSchema<T> {
4343
if (REF_KEY in schema || DEPENDENCIES_KEY in schema || ALL_OF_KEY in schema) {
4444
const _schema = retrieveSchema<T, S, F>(validator, schema, rootSchema, formData, experimental_customMergeAllOf);
45-
const sameSchemaIndex = _recurseList.findIndex((item) => isEqual(item, _schema));
45+
const sameSchemaIndex = _recurseList.findIndex((item) => deepEquals(item, _schema));
4646
if (sameSchemaIndex === -1) {
4747
return toIdSchemaInternal<T, S, F>(
4848
validator,

packages/utils/src/schema/toPathSchema.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
11
import get from 'lodash/get';
2-
import isEqual from 'lodash/isEqual';
32
import set from 'lodash/set';
43

54
import {
5+
ADDITIONAL_PROPERTIES_KEY,
66
ALL_OF_KEY,
77
ANY_OF_KEY,
8-
ADDITIONAL_PROPERTIES_KEY,
98
DEPENDENCIES_KEY,
109
ITEMS_KEY,
1110
NAME_KEY,
@@ -26,6 +25,7 @@ import {
2625
} from '../types';
2726
import getClosestMatchingOption from './getClosestMatchingOption';
2827
import retrieveSchema from './retrieveSchema';
28+
import deepEquals from '../deepEquals';
2929

3030
/** An internal helper that generates an `PathSchema` object for the `schema`, recursively with protection against
3131
* infinite recursion
@@ -50,7 +50,7 @@ function toPathSchemaInternal<T = any, S extends StrictRJSFSchema = RJSFSchema,
5050
): PathSchema<T> {
5151
if (REF_KEY in schema || DEPENDENCIES_KEY in schema || ALL_OF_KEY in schema) {
5252
const _schema = retrieveSchema<T, S, F>(validator, schema, rootSchema, formData, experimental_customMergeAllOf);
53-
const sameSchemaIndex = _recurseList.findIndex((item) => isEqual(item, _schema));
53+
const sameSchemaIndex = _recurseList.findIndex((item) => deepEquals(item, _schema));
5454
if (sameSchemaIndex === -1) {
5555
return toPathSchemaInternal<T, S, F>(
5656
validator,

packages/validator-ajv8/src/precompiledValidator.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,22 @@
11
import { ErrorObject } from 'ajv';
22
import get from 'lodash/get';
3-
import isEqual from 'lodash/isEqual';
3+
44
import {
55
CustomValidator,
6+
deepEquals,
67
ErrorSchema,
78
ErrorTransformer,
89
FormContextType,
910
hashForSchema,
1011
ID_KEY,
1112
JUNK_OPTION_ID,
13+
retrieveSchema,
1214
RJSFSchema,
1315
StrictRJSFSchema,
1416
toErrorList,
1517
UiSchema,
1618
ValidationData,
1719
ValidatorType,
18-
retrieveSchema,
1920
} from '@rjsf/utils';
2021

2122
import { CompiledValidateFunction, Localizer, ValidatorFunctions } from './types';
@@ -92,10 +93,10 @@ export default class AJV8PrecompiledValidator<
9293
* @param [formData] - The form data to validate if any
9394
*/
9495
ensureSameRootSchema(schema: S, formData?: T) {
95-
if (!isEqual(schema, this.rootSchema)) {
96+
if (!deepEquals(schema, this.rootSchema)) {
9697
// Resolve the root schema with the passed in form data since that may affect the resolution
9798
const resolvedRootSchema = retrieveSchema(this, this.rootSchema, this.rootSchema, formData);
98-
if (!isEqual(schema, resolvedRootSchema)) {
99+
if (!deepEquals(schema, resolvedRootSchema)) {
99100
throw new Error(
100101
'The schema associated with the precompiled validator differs from the rootSchema provided for validation'
101102
);

0 commit comments

Comments
 (0)