Skip to content

Commit 9f4a7d4

Browse files
Reimplement rjsf-team#1755 and and rjsf-team#2460 in new Form.tsx
- Reimplement two fixes related to `onChange` in the new `Form.tsx` - rjsf-team#1755 fixes the calling of the proper `onChange` during props update - rjsf-team#2460 fixes race condition on calling `onChange` after setState.
1 parent 2ab02d8 commit 9f4a7d4

File tree

3 files changed

+109
-4
lines changed

3 files changed

+109
-4
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ should change the heading of the (upcoming) version to include a major version b
3333
- Added `ui:duplicateKeySuffixSeparator` to customize how duplicate object keys are renamed when using `additionalProperties`.
3434
- The `extraErrors` are now consistently appended onto the end of the schema validation-based `errors` information that is returned via the `onErrors()` callback when submit fails.
3535
- In addition, the extra information provided by AJV is no longer stripped from the `errors` during the merge process, fixing (https://github.com/rjsf-team/react-jsonschema-form/issues/1596).
36+
- Correctly call the `onChange` handler in the new set of props if it changed, fixing (https://github.com/rjsf-team/react-jsonschema-form/issues/1708).
3637

3738
## @rjsf/antd
3839
- Fix esm build to use `@rollup/plugin-replace` to replace `antd/lib` and `rc-picker/lib` with `antd/es` and `rc-picker/es` respectively, fixing (https://github.com/rjsf-team/react-jsonschema-form/issues/2962)

packages/core/src/components/Form.tsx

+3-3
Original file line numberDiff line numberDiff line change
@@ -260,9 +260,9 @@ export default class Form<T = any, F = any> extends Component<
260260
if (
261261
!deepEquals(nextState.formData, nextProps.formData) &&
262262
!deepEquals(nextState.formData, this.state.formData) &&
263-
this.props.onChange
263+
nextProps.onChange
264264
) {
265-
this.props.onChange(nextState);
265+
nextProps.onChange(nextState);
266266
}
267267
this.setState(nextState);
268268
}
@@ -562,7 +562,7 @@ export default class Form<T = any, F = any> extends Component<
562562
}
563563
this.setState(
564564
state as FormState<T, F>,
565-
() => onChange && onChange(this.state)
565+
() => onChange && onChange({ ...this.state, ...state })
566566
);
567567
};
568568

packages/core/test/Form_test.js

+105-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { expect } from "chai";
22
import sinon from "sinon";
33
import React, { createRef } from "react";
4-
import { renderIntoDocument, Simulate } from "react-dom/test-utils";
4+
import { renderIntoDocument, act, Simulate } from "react-dom/test-utils";
55
import { render, findDOMNode } from "react-dom";
66
import { Portal } from "react-portal";
77
import validator, { customizeValidator } from "@rjsf/validator-ajv6";
@@ -1012,6 +1012,52 @@ describeRepeated("Form common", (createFormComponent) => {
10121012
uiSchema,
10131013
});
10141014
});
1015+
it("should call last provided change handler", async () => {
1016+
const schema = {
1017+
type: "object",
1018+
properties: {
1019+
foo: {
1020+
type: "string",
1021+
default: "bar",
1022+
},
1023+
},
1024+
};
1025+
1026+
const secondOnChange = sandbox.spy();
1027+
1028+
const { comp, onChange } = createFormComponent({
1029+
schema,
1030+
formData: { foo: "bar1" },
1031+
});
1032+
1033+
act(() => {
1034+
setProps(comp, {
1035+
schema,
1036+
formData: {},
1037+
onChange,
1038+
});
1039+
});
1040+
1041+
sinon.assert.callCount(onChange, 1);
1042+
1043+
act(() => {
1044+
setProps(comp, {
1045+
schema,
1046+
formData: { foo: "bar2" },
1047+
});
1048+
});
1049+
1050+
act(() => {
1051+
setProps(comp, {
1052+
schema,
1053+
formData: {},
1054+
onChange: secondOnChange,
1055+
});
1056+
});
1057+
1058+
sinon.assert.callCount(onChange, 1);
1059+
sinon.assert.callCount(secondOnChange, 1);
1060+
});
10151061
});
10161062
describe("Blur handler", () => {
10171063
it("should call provided blur handler on form input blur event", () => {
@@ -3402,4 +3448,62 @@ describe("Form omitExtraData and liveOmit", () => {
34023448

34033449
expect(node.querySelectorAll(".error-detail li")).to.have.length.of(2);
34043450
});
3451+
describe("Calling onChange right after updating a Form with props formData", () => {
3452+
const schema = {
3453+
type: "array",
3454+
items: {
3455+
type: "string",
3456+
},
3457+
};
3458+
3459+
let changed = false;
3460+
class ArrayThatTriggersOnChangeRightAfterUpdated extends React.Component {
3461+
componentDidUpdate = () => {
3462+
if (changed) {
3463+
return;
3464+
}
3465+
changed = true;
3466+
this.props.onChange([...this.props.formData, "test"]);
3467+
};
3468+
render() {
3469+
const { ArrayField } = this.props.registry.fields;
3470+
return <ArrayField {...this.props} />;
3471+
}
3472+
}
3473+
3474+
const uiSchema = {
3475+
"ui:field": ArrayThatTriggersOnChangeRightAfterUpdated,
3476+
};
3477+
3478+
const props = {
3479+
schema,
3480+
uiSchema,
3481+
};
3482+
3483+
class Container extends React.Component {
3484+
constructor(props) {
3485+
super(props);
3486+
this.state = {};
3487+
}
3488+
3489+
onChange = ({ formData }) => {
3490+
this.setState({ formData });
3491+
};
3492+
3493+
render() {
3494+
return (
3495+
<Form {...this.props} {...this.state} onChange={this.onChange} />
3496+
);
3497+
}
3498+
}
3499+
3500+
it("doesn't cause a race condition", () => {
3501+
const { node } = createComponent(Container, { ...props });
3502+
3503+
Simulate.click(node.querySelector(".array-item-add button"));
3504+
3505+
expect(node.querySelector("#root_0")).to.exist;
3506+
expect(node.querySelector("#root_1").getAttribute("value")).to.eq("test");
3507+
});
3508+
});
34053509
});

0 commit comments

Comments
 (0)