diff --git a/CHANGELOG.md b/CHANGELOG.md
index 0735abc61f..2ec580441b 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -16,6 +16,18 @@ should change the heading of the (upcoming) version to include a major version b
-->
+# 5.22.2
+
+## @rjsf/core
+
+- Fixed validation regression Form not revalidating after formData change, fixing [#4343](https://github.com/rjsf-team/react-jsonschema-form/issues/4343)
+
+# 5.22.1
+
+## Dev / docs / playground
+
+- Bumped peer dependencies to 5.22.x due to updated type definition and API changes in @rjsf/utils
+
# 5.22.0
## @rjsf/core
diff --git a/packages/core/src/components/Form.tsx b/packages/core/src/components/Form.tsx
index d9ea45529d..7bc39f6c83 100644
--- a/packages/core/src/components/Form.tsx
+++ b/packages/core/src/components/Form.tsx
@@ -441,9 +441,9 @@ export default class Form<
if (mustValidate) {
const schemaValidation = this.validate(formData, schema, schemaUtils, _retrievedSchema);
errors = schemaValidation.errors;
- // If the schema has changed, we do not merge state.errorSchema.
+ // If retrievedSchema is undefined which means the schema or formData has changed, we do not merge state.
// Else in the case where it hasn't changed, we merge 'state.errorSchema' with 'schemaValidation.errorSchema.' This done to display the raised field error.
- if (isSchemaChanged) {
+ if (retrievedSchema === undefined) {
errorSchema = schemaValidation.errorSchema;
} else {
errorSchema = mergeObjects(
diff --git a/packages/core/test/ArrayField.test.jsx b/packages/core/test/ArrayField.test.jsx
index c80a65ee32..7dec35974a 100644
--- a/packages/core/test/ArrayField.test.jsx
+++ b/packages/core/test/ArrayField.test.jsx
@@ -3219,6 +3219,27 @@ describe('ArrayField', () => {
});
});
+ it('Check that when formData changes, the form should re-validate', () => {
+ const { node, rerender } = createFormComponent({
+ schema,
+ formData: [
+ {
+ text: null,
+ },
+ ],
+ liveValidate: true,
+ });
+
+ const errorMessages = node.querySelectorAll('#root_0_text__error');
+ expect(errorMessages).to.have.length(1);
+ const errorMessageContent = node.querySelector('#root_0_text__error .text-danger').textContent;
+ expect(errorMessageContent).to.contain('must be string');
+
+ rerender({ schema, formData: [{ text: 'test' }], liveValidate: true });
+
+ expect(node.querySelectorAll('#root_0_text__error')).to.have.length(0);
+ });
+
it('raise an error and check if the error is displayed', () => {
const { node } = createFormComponent({
schema,
diff --git a/packages/core/test/ObjectField.test.jsx b/packages/core/test/ObjectField.test.jsx
index 89a83d9b63..fc0cfd08ab 100644
--- a/packages/core/test/ObjectField.test.jsx
+++ b/packages/core/test/ObjectField.test.jsx
@@ -227,6 +227,25 @@ describe('ObjectField', () => {
});
});
+ it('Check that when formData changes, the form should re-validate', () => {
+ const { node, rerender } = createFormComponent({
+ schema,
+ formData: {
+ foo: null,
+ },
+ liveValidate: true,
+ });
+
+ const errorMessages = node.querySelectorAll('#root_foo__error');
+ expect(errorMessages).to.have.length(1);
+ const errorMessageContent = node.querySelector('#root_foo__error .text-danger').textContent;
+ expect(errorMessageContent).to.contain('must be string');
+
+ rerender({ schema, formData: { foo: 'test' }, liveValidate: true });
+
+ expect(node.querySelectorAll('#root_foo__error')).to.have.length(0);
+ });
+
it('raise an error and check if the error is displayed', () => {
const { node } = createFormComponent({
schema,
diff --git a/packages/core/test/StringField.test.jsx b/packages/core/test/StringField.test.jsx
index f14d195d67..3449ab1278 100644
--- a/packages/core/test/StringField.test.jsx
+++ b/packages/core/test/StringField.test.jsx
@@ -299,6 +299,23 @@ describe('StringField', () => {
expect(node.querySelector('input').getAttribute('autocomplete')).eql('family-name');
});
+ it('Check that when formData changes, the form should re-validate', () => {
+ const { node, rerender } = createFormComponent({
+ schema: { type: 'string' },
+ formData: null,
+ liveValidate: true,
+ });
+
+ const errorMessages = node.querySelectorAll('#root__error');
+ expect(errorMessages).to.have.length(1);
+ const errorMessageContent = node.querySelector('#root__error .text-danger').textContent;
+ expect(errorMessageContent).to.contain('must be string');
+
+ rerender({ schema: { type: 'string' }, formData: 'hello', liveValidate: true });
+
+ expect(node.querySelectorAll('#root__error')).to.have.length(0);
+ });
+
it('raise an error and check if the error is displayed', () => {
const { node } = createFormComponent({
schema: { type: 'string' },
diff --git a/packages/core/test/test_utils.js b/packages/core/test/test_utils.js
index 3db1807bf2..b859221db5 100644
--- a/packages/core/test/test_utils.js
+++ b/packages/core/test/test_utils.js
@@ -14,10 +14,13 @@ export function createComponent(Component, props) {
const onError = sinon.spy();
const onSubmit = sinon.spy();
const comp = ;
- const { container } = render(comp);
+ const { container, rerender } = render(comp);
+
+ const rerenderFunction = (props) =>
+ rerender();
const node = findDOMNode(container).firstElementChild;
- return { comp, node, onChange, onError, onSubmit };
+ return { comp, node, onChange, onError, onSubmit, rerender: rerenderFunction };
}
export function createFormComponent(props) {