Skip to content

Commit a5a0a89

Browse files
committed
Fixes #231: Reset ObjectField state on diffing formData keys. (#234)
1 parent 3da2505 commit a5a0a89

File tree

4 files changed

+95
-7
lines changed

4 files changed

+95
-7
lines changed

devServer.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,10 @@ app.use(require("webpack-dev-middleware")(compiler, {
1919

2020
app.use(require("webpack-hot-middleware")(compiler));
2121

22+
app.get("/favicon.ico", function(req, res) {
23+
res.status(204).end();
24+
});
25+
2226
app.get("/", function(req, res) {
2327
res.sendFile(path.join(__dirname, "playground", "index.html"));
2428
});

src/components/Form.js

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,16 @@ export default class Form extends Component {
4545
errorSchema: state.errorSchema || {}
4646
};
4747
const idSchema = toIdSchema(schema, uiSchema["ui:rootFieldId"], definitions);
48-
return {status: "initial", formData, edit, errors, errorSchema, idSchema};
48+
return {
49+
status: "initial",
50+
schema,
51+
uiSchema,
52+
idSchema,
53+
formData,
54+
edit,
55+
errors,
56+
errorSchema
57+
};
4958
}
5059

5160
shouldComponentUpdate(nextProps, nextState) {
@@ -120,8 +129,6 @@ export default class Form extends Component {
120129
render() {
121130
const {
122131
children,
123-
schema,
124-
uiSchema,
125132
safeRenderCompletion,
126133
id,
127134
className,
@@ -134,7 +141,7 @@ export default class Form extends Component {
134141
acceptcharset
135142
} = this.props;
136143

137-
const {formData, errorSchema, idSchema} = this.state;
144+
const {schema, uiSchema, formData, errorSchema, idSchema} = this.state;
138145
const registry = this.getRegistry();
139146
const _SchemaField = registry.fields.SchemaField;
140147

src/components/fields/ObjectField.js

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
import React, { Component, PropTypes } from "react";
2+
import deeper from "deeper";
3+
24

35
import {
46
getDefaultFormState,
@@ -10,6 +12,20 @@ import {
1012
} from "../../utils";
1113

1214

15+
function objectKeysHaveChanged(formData, state) {
16+
// for performance, first check for lengths
17+
const newKeys = Object.keys(formData);
18+
const oldKeys = Object.keys(state);
19+
if (newKeys.length < oldKeys.length) {
20+
return true;
21+
}
22+
// deep check on sorted keys
23+
if (!deeper(newKeys.sort(), oldKeys.sort())) {
24+
return true;
25+
}
26+
return false;
27+
}
28+
1329
class ObjectField extends Component {
1430
static defaultProps = {
1531
uiSchema: {},
@@ -27,7 +43,16 @@ class ObjectField extends Component {
2743
}
2844

2945
componentWillReceiveProps(nextProps) {
30-
this.setState(this.getStateFromProps(nextProps));
46+
const state = this.getStateFromProps(nextProps);
47+
const {formData} = nextProps;
48+
if (formData && objectKeysHaveChanged(formData, this.state)) {
49+
// We *need* to replace state entirely here has we have received formData
50+
// holding different keys (so with some removed).
51+
this.state = state;
52+
this.forceUpdate();
53+
} else {
54+
this.setState(state);
55+
}
3156
}
3257

3358
getStateFromProps(props) {

test/Form_test.js

Lines changed: 54 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -662,7 +662,7 @@ describe("Form", () => {
662662
expect(comp.state.errorSchema).eql({
663663
__errors: [
664664
"does not meet minimum length of 8",
665-
`does not match pattern "\d+"`
665+
"does not match pattern \"\d+\""
666666
]
667667
});
668668
});
@@ -675,7 +675,7 @@ describe("Form", () => {
675675

676676
expect(errors).eql([
677677
"does not meet minimum length of 8",
678-
`does not match pattern "\d+"`
678+
"does not match pattern \"\d+\""
679679
]);
680680
});
681681
});
@@ -926,6 +926,58 @@ describe("Form", () => {
926926
});
927927
});
928928

929+
describe("Schema and formData updates", () => {
930+
// https://github.com/mozilla-services/react-jsonschema-form/issues/231
931+
const schema = {
932+
type: "object",
933+
properties: {
934+
foo: {type: "string"},
935+
bar: {type: "string"},
936+
}
937+
};
938+
939+
it("should replace state when formData have keys removed", () => {
940+
const formData = {foo: "foo", bar: "bar"};
941+
const {comp, node} = createFormComponent({schema, formData});
942+
comp.componentWillReceiveProps({
943+
schema: {
944+
type: "object",
945+
properties: {
946+
bar: {type: "string"},
947+
}
948+
},
949+
formData: {bar: "bar"},
950+
});
951+
952+
Simulate.change(node.querySelector("#root_bar"), {
953+
target: {value: "baz"}
954+
});
955+
956+
expect(comp.state.formData).eql({bar: "baz"});
957+
});
958+
959+
it("should replace state when formData keys have changed", () => {
960+
const formData = {foo: "foo", bar: "bar"};
961+
const {comp, node} = createFormComponent({schema, formData});
962+
comp.componentWillReceiveProps({
963+
schema: {
964+
type: "object",
965+
properties: {
966+
foo: {type: "string"},
967+
baz: {type: "string"},
968+
}
969+
},
970+
formData: {foo: "foo", baz: "bar"},
971+
});
972+
973+
Simulate.change(node.querySelector("#root_baz"), {
974+
target: {value: "baz"}
975+
});
976+
977+
expect(comp.state.formData).eql({foo: "foo", baz: "baz"});
978+
});
979+
});
980+
929981
describe("Attributes", () => {
930982
const formProps = {
931983
schema: {},

0 commit comments

Comments
 (0)