Form fields lose focus when input value changes
The problem is pretty straightforward, you are creating a FormWithConditionals
component in your render method and in your onChange
handler you setState
which triggers a re-render and thus a new instance of FormWithConditionals
is created and hence it loses focus. You need to move this instance out of render method and perhaps out of the component itself since it uses static values.
As schema
, uiSchema
and rules
are passed as props to the ConditionalForm
, you can create an instance of FormWithConditionals
in constructor
function and use it in render like this
import React from 'react';
import PropTypes from 'prop-types';
import Engine from "json-rules-engine-simplified";
import Form from "react-jsonschema-form";
import applyRules from "react-jsonschema-form-conditionals";
function FormModelInspector (props) {
return (
<div>
<div className="checkbox">
<label>
<input type="checkbox" onChange={props.onChange} checked={props.showModel}/>
Show Form Model
</label>
</div>
{
props.showModel && <pre>{JSON.stringify(props.formData, null, 2)}</pre>
}
</div>
)
}
class ConditionalForm extends React.Component {
constructor (props) {
super(props);
this.state = {
formData: {},
showModel: true
};
const { schema, uiSchema, rules } = props;
this.FormWithConditionals = applyRules(schema, uiSchema, rules, Engine)(Form);
this.handleFormDataChange = this.handleFormDataChange.bind(this);
this.handleShowModelChange = this.handleShowModelChange.bind(this);
}
handleShowModelChange (event) {
this.setState({showModel: event.target.checked});
}
handleFormDataChange ({formData}) {
this.setState({formData});
}
render () {
const FormWithConditionals = this.FormWithConditionals;
return (
<div className="row">
<div className="col-md-6">
<FormWithConditionals schema={schema}
uiSchema={uiSchema}
formData={this.state.formData}
onChange={this.handleFormDataChange}
noHtml5Validate={true}>
</FormWithConditionals>
</div>
<div className="col-md-6">
<FormModelInspector formData={this.state.formData}
showModel={this.state.showModel}
onChange={this.handleShowModelChange}/>
</div>
</div>
);
}
}
ConditionalForm.propTypes = {
schema: PropTypes.object.isRequired,
uiSchema: PropTypes.object.isRequired,
rules: PropTypes.array.isRequired
};
ConditionalForm.defaultProps = {
uiSchema: {},
rules: []
};
For anyone bumping into the same problem but using Hooks, here's how without a class :
Just use a variable declared outside the component and initialize it inside useEffect
. (don't forget to pass []
as second parameter to tell react that we do not depend on any variable, replicating the componentWillMount
effect)
// import ...
import Engine from 'json-rules-engine-simplified'
import Form from 'react-jsonschema-form'
let FormWithConditionals = () => null
const MyComponent = (props) => {
const {
formData,
schema,
uischema,
rules,
} = props;
useEffect(() => {
FormWithConditionals = applyRules(schema, uischema, rules, Engine)(Form)
}, [])
return (
<FormWithConditionals>
<div></div>
</FormWithConditionals>
);
}
export default MyComponent