diff --git a/CHANGELOG.md b/CHANGELOG.md index d284f8c924..0ab74bf417 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,10 @@ should change the heading of the (upcoming) version to include a major version b --> # 5.14.3 +## @rjsf/core + +- add `retrieveSchema` at `Form` state to memoize the result of `schemUtils.retrieveSchema` + ## @rjsf/fluentui-rc - Updated README.md references - Fixed width of `ArrayFieldItemTemplate` items diff --git a/packages/core/src/components/Form.tsx b/packages/core/src/components/Form.tsx index f46b8eb6de..a8180521ef 100644 --- a/packages/core/src/components/Form.tsx +++ b/packages/core/src/components/Form.tsx @@ -238,6 +238,9 @@ export interface FormState; + // Private + /** @description result of schemaUtils.retrieveSchema(schema, formData). This a memoized value to avoid re calculate at internal functions (getStateFromProps, onChange) */ + retrievedSchema: S; } /** The event data passed when changes have been made to the form, includes everything from the `FormState` except @@ -303,7 +306,11 @@ export default class Form< prevState: FormState ): { nextState: FormState; shouldUpdate: true } | { shouldUpdate: false } { if (!deepEquals(this.props, prevProps)) { - const nextState = this.getStateFromProps(this.props, this.props.formData); + const nextState = this.getStateFromProps( + this.props, + this.props.formData, + prevProps.schema !== this.props.schema ? undefined : this.state.retrievedSchema + ); const shouldUpdate = !deepEquals(nextState, prevState); return { nextState, shouldUpdate }; } @@ -352,7 +359,7 @@ export default class Form< * @param inputFormData - The new or current data for the `Form` * @returns - The new state for the `Form` */ - getStateFromProps(props: FormProps, inputFormData?: T): FormState { + getStateFromProps(props: FormProps, inputFormData?: T, retrievedSchema?: S): FormState { const state: FormState = this.state || {}; const schema = 'schema' in props ? props.schema : this.props.schema; const uiSchema: UiSchema = ('uiSchema' in props ? props.uiSchema! : this.props.uiSchema!) || {}; @@ -372,7 +379,7 @@ export default class Form< schemaUtils = createSchemaUtils(props.validator, rootSchema, experimental_defaultFormStateBehavior); } const formData: T = schemaUtils.getDefaultFormState(schema, inputFormData) as T; - const retrievedSchema = schemaUtils.retrieveSchema(schema, formData); + const _retrievedSchema = retrievedSchema ?? schemaUtils.retrieveSchema(schema, formData); const getCurrentErrors = (): ValidationData => { if (props.noValidate) { @@ -394,7 +401,7 @@ export default class Form< let schemaValidationErrors: RJSFValidationError[] = state.schemaValidationErrors; let schemaValidationErrorSchema: ErrorSchema = state.schemaValidationErrorSchema; if (mustValidate) { - const schemaValidation = this.validate(formData, schema, schemaUtils, retrievedSchema); + const schemaValidation = this.validate(formData, schema, schemaUtils, _retrievedSchema); errors = schemaValidation.errors; errorSchema = schemaValidation.errorSchema; schemaValidationErrors = errors; @@ -410,7 +417,7 @@ export default class Form< errors = merged.errors; } const idSchema = schemaUtils.toIdSchema( - retrievedSchema, + _retrievedSchema, uiSchema['ui:rootFieldId'], formData, props.idPrefix, @@ -427,6 +434,7 @@ export default class Form< errorSchema, schemaValidationErrors, schemaValidationErrorSchema, + retrievedSchema: _retrievedSchema, }; return nextState; } @@ -550,9 +558,10 @@ export default class Form< */ onChange = (formData: T | undefined, newErrorSchema?: ErrorSchema, id?: string) => { const { extraErrors, omitExtraData, liveOmit, noValidate, liveValidate, onChange } = this.props; - const { schemaUtils, schema } = this.state; + const { schemaUtils, schema, retrievedSchema } = this.state; + if (isObject(formData) || Array.isArray(formData)) { - const newState = this.getStateFromProps(this.props, formData); + const newState = this.getStateFromProps(this.props, formData, retrievedSchema); formData = newState.formData; } @@ -560,9 +569,10 @@ export default class Form< let state: Partial> = { formData, schema }; let newFormData = formData; + let _retrievedSchema: S | undefined; if (omitExtraData === true && liveOmit === true) { - const retrievedSchema = schemaUtils.retrieveSchema(schema, formData); - const pathSchema = schemaUtils.toPathSchema(retrievedSchema, '', formData); + _retrievedSchema = schemaUtils.retrieveSchema(schema, formData); + const pathSchema = schemaUtils.toPathSchema(_retrievedSchema, '', formData); const fieldNames = this.getFieldNames(pathSchema, formData); @@ -573,7 +583,7 @@ export default class Form< } if (mustValidate) { - const schemaValidation = this.validate(newFormData); + const schemaValidation = this.validate(newFormData, schema, schemaUtils, retrievedSchema); let errors = schemaValidation.errors; let errorSchema = schemaValidation.errorSchema; const schemaValidationErrors = errors; @@ -600,6 +610,9 @@ export default class Form< errors: toErrorList(errorSchema), }; } + if (_retrievedSchema) { + state.retrievedSchema = _retrievedSchema; + } this.setState(state as FormState, () => onChange && onChange({ ...this.state, ...state }, id)); }; diff --git a/packages/core/test/Form.test.jsx b/packages/core/test/Form.test.jsx index c9a28c2fe3..6659e109e2 100644 --- a/packages/core/test/Form.test.jsx +++ b/packages/core/test/Form.test.jsx @@ -140,6 +140,7 @@ describeRepeated('Form common', (createFormComponent) => { schemaValidationErrors: undefined, schemaValidationErrorSchema: undefined, schemaUtils: sinon.match.object, + retrievedSchema: schema, }); }); }); @@ -1480,6 +1481,7 @@ describeRepeated('Form common', (createFormComponent) => { schemaValidationErrors: undefined, schemaValidationErrorSchema: undefined, schemaUtils: sinon.match.object, + retrievedSchema: formProps.schema, }); }); });