Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: formData change clear errorMessage #4429

Merged
merged 8 commits into from
Jan 9, 2025
Merged
22 changes: 19 additions & 3 deletions packages/core/src/components/Form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
ErrorTransformer,
FormContextType,
GenericObjectType,
getChangedFields,
getTemplate,
getUiOptions,
IdSchema,
Expand Down Expand Up @@ -316,16 +317,18 @@ export default class Form<
prevState: FormState<T, S, F>
): { nextState: FormState<T, S, F>; shouldUpdate: true } | { shouldUpdate: false } {
if (!deepEquals(this.props, prevProps)) {
const formDataChangedFields = getChangedFields(this.props.formData, prevProps.formData);
const isSchemaChanged = !deepEquals(prevProps.schema, this.props.schema);
const isFormDataChanged = !deepEquals(prevProps.formData, this.props.formData);
const isFormDataChanged = formDataChangedFields.length > 0;
const nextState = this.getStateFromProps(
this.props,
this.props.formData,
// If the `schema` has changed, we need to update the retrieved schema.
// Or if the `formData` changes, for example in the case of a schema with dependencies that need to
// match one of the subSchemas, the retrieved schema must be updated.
isSchemaChanged || isFormDataChanged ? undefined : this.state.retrievedSchema,
isSchemaChanged
isSchemaChanged,
formDataChangedFields
);
const shouldUpdate = !deepEquals(nextState, prevState);
return { nextState, shouldUpdate };
Expand Down Expand Up @@ -375,13 +378,15 @@ export default class Form<
* @param inputFormData - The new or current data for the `Form`
* @param retrievedSchema - An expanded schema, if not provided, it will be retrieved from the `schema` and `formData`.
* @param isSchemaChanged - A flag indicating whether the schema has changed.
* @param formDataChangedFields - The changed fields of `formData`
* @returns - The new state for the `Form`
*/
getStateFromProps(
props: FormProps<T, S, F>,
inputFormData?: T,
retrievedSchema?: S,
isSchemaChanged = false
isSchemaChanged = false,
formDataChangedFields: string[] = []
): FormState<T, S, F> {
const state: FormState<T, S, F> = this.state || {};
const schema = 'schema' in props ? props.schema : this.props.schema;
Expand Down Expand Up @@ -458,6 +463,17 @@ export default class Form<
const currentErrors = getCurrentErrors();
errors = currentErrors.errors;
errorSchema = currentErrors.errorSchema;
if (formDataChangedFields.length > 0) {
const newErrorSchema = formDataChangedFields.reduce((acc, key) => {
acc[key] = undefined;
return acc;
}, {} as Record<string, undefined>);
errorSchema = schemaValidationErrorSchema = mergeObjects(
currentErrors.errorSchema,
newErrorSchema,
'preventDuplicates'
) as ErrorSchema<T>;
}
}
if (props.extraErrors) {
const merged = validationDataMerge({ errorSchema, errors }, props.extraErrors);
Expand Down
23 changes: 23 additions & 0 deletions packages/utils/src/getChangedFields.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import keys from 'lodash/keys';
import pickBy from 'lodash/pickBy';
import isEqual from 'lodash/isEqual';

/**
* Compares two objects and returns the names of the fields that have changed.
* This function iterates over each field of object `a`, using `_.isEqual` to compare the field value
* with the corresponding field value in object `b`. If the values are different, the field name will
* be included in the returned array.
*
* @param {any} a - The first object, representing the original data to compare.
* @param {any} b - The second object, representing the updated data to compare.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
* @param {any} a - The first object, representing the original data to compare.
* @param {any} b - The second object, representing the updated data to compare.
* @param a - The first object, representing the original data to compare.
* @param b - The second object, representing the updated data to compare.

Copy link
Contributor Author

@JinYuSha0 JinYuSha0 Jan 5, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I'm done with this

* @returns {string[]} - An array of field names that have changed.
*
* @example
* const a = { name: 'John', age: 30 };
* const b = { name: 'John', age: 31 };
* const changedFields = getChangedFields(a, b);
* console.log(changedFields); // Output: ['age']
*/
export default function getChangedFields(a: any, b: any): string[] {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For better Typescript compatibility, unknown is preferred over any

Suggested change
export default function getChangedFields(a: any, b: any): string[] {
export default function getChangedFields(a: unknown, b: unknown): string[] {

return keys(pickBy(a, (value, key) => !isEqual(value, b[key])));
}
2 changes: 2 additions & 0 deletions packages/utils/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ import utcToLocal from './utcToLocal';
import validationDataMerge from './validationDataMerge';
import withIdRefPrefix from './withIdRefPrefix';
import getOptionMatchingSimpleDiscriminator from './getOptionMatchingSimpleDiscriminator';
import getChangedFields from './getChangedFields';

export * from './types';
export * from './enums';
Expand Down Expand Up @@ -82,6 +83,7 @@ export {
examplesId,
ErrorSchemaBuilder,
findSchemaDefinition,
getChangedFields,
getDateElementProps,
getDiscriminatorFieldFromSchema,
getInputProps,
Expand Down
Loading