Skip to content

Commit ea0b83a

Browse files
Attempting to fix 3616 by optimizing live validation (#4738)
* Attempting to fix 3616 by optimizing live validation - Updated `Form` to add live validation optimization in a few cases where it runs unnecessarily - Updated `CHANGELOG.md` accordingly * - Another optimization * Update packages/core/src/components/Form.tsx
1 parent 558558e commit ea0b83a

File tree

2 files changed

+15
-3
lines changed

2 files changed

+15
-3
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ should change the heading of the (upcoming) version to include a major version b
2424
## @rjsf/core
2525

2626
- Updated `ArrayField` `onSelectChange` to not pass `name` in the `path` since the `ObjectField` will automatically add it [#4733](https://github.com/rjsf-team/react-jsonschema-form/issues/4733)
27+
- Updated `Form` to optimize the need for live validation in an attempt to improve performance, potentially fixing [#3616](https://github.com/rjsf-team/react-jsonschema-form/issues/3616)
2728

2829
## @rjsf/semantic-ui
2930

packages/core/src/components/Form.tsx

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -345,12 +345,18 @@ export default class Form<
345345
prevState: FormState<T, S, F>,
346346
): { nextState: FormState<T, S, F>; shouldUpdate: true } | { shouldUpdate: false } {
347347
if (!deepEquals(this.props, prevProps)) {
348+
// Compare the previous props formData against the current props formData
348349
const formDataChangedFields = getChangedFields(this.props.formData, prevProps.formData);
350+
// Compare the current props formData against the current state's formData to determine if the new props were the
351+
// result of the onChange from the existing state formData
352+
const stateDataChangedFields = getChangedFields(this.props.formData, this.state.formData);
349353
const isSchemaChanged = !deepEquals(prevProps.schema, this.props.schema);
350354
// When formData is not an object, getChangedFields returns an empty array.
351355
// In this case, deepEquals is most needed to check again.
352356
const isFormDataChanged =
353357
formDataChangedFields.length > 0 || !deepEquals(prevProps.formData, this.props.formData);
358+
const isStateDataChanged =
359+
stateDataChangedFields.length > 0 || !deepEquals(this.state.formData, this.props.formData);
354360
const nextState = this.getStateFromProps(
355361
this.props,
356362
this.props.formData,
@@ -360,6 +366,8 @@ export default class Form<
360366
isSchemaChanged || isFormDataChanged ? undefined : this.state.retrievedSchema,
361367
isSchemaChanged,
362368
formDataChangedFields,
369+
// Skip live validation for this request if no form data has changed from the last state
370+
!isStateDataChanged,
363371
);
364372
const shouldUpdate = !deepEquals(nextState, prevState);
365373
return { nextState, shouldUpdate };
@@ -415,14 +423,15 @@ export default class Form<
415423
retrievedSchema?: S,
416424
isSchemaChanged = false,
417425
formDataChangedFields: string[] = [],
426+
skipLiveValidate = false,
418427
): FormState<T, S, F> {
419428
const state: FormState<T, S, F> = this.state || {};
420429
const schema = 'schema' in props ? props.schema : this.props.schema;
421430
const validator = 'validator' in props ? props.validator : this.props.validator;
422431
const uiSchema: UiSchema<T, S, F> = ('uiSchema' in props ? props.uiSchema! : this.props.uiSchema!) || {};
423432
const edit = typeof inputFormData !== 'undefined';
424433
const liveValidate = 'liveValidate' in props ? props.liveValidate : this.props.liveValidate;
425-
const mustValidate = edit && !props.noValidate && liveValidate;
434+
const mustValidate = edit && !props.noValidate && liveValidate && !skipLiveValidate;
426435
const experimental_defaultFormStateBehavior =
427436
'experimental_defaultFormStateBehavior' in props
428437
? props.experimental_defaultFormStateBehavior
@@ -770,7 +779,8 @@ export default class Form<
770779
// If the newValue is not on the root path, then set it into the form data
771780
_set(formData, path, newValue);
772781
}
773-
const newState = this.getStateFromProps(this.props, formData);
782+
// Pass true to skip live validation in `getStateFromProps()` since we will do it a bit later
783+
const newState = this.getStateFromProps(this.props, formData, undefined, undefined, undefined, true);
774784
formData = newState.formData;
775785
retrievedSchema = newState.retrievedSchema;
776786
}
@@ -793,7 +803,8 @@ export default class Form<
793803
_set(errorSchemaCopy, path, newErrorSchema);
794804
newErrorSchema = errorSchemaCopy;
795805
}
796-
if (mustValidate) {
806+
// If there are pending changes in the queue, skip live validation since it will happen with the last change
807+
if (mustValidate && this.pendingChanges.length === 1) {
797808
const schemaValidation = this.validate(newFormData, schema, schemaUtils, retrievedSchema);
798809
let errors = schemaValidation.errors;
799810
let errorSchema = schemaValidation.errorSchema;

0 commit comments

Comments
 (0)