[英]Form fields lose focus when input value changes
I'm trying to build a form with conditional fields from a JSON schema using react-jsonschema-form and react-jsonschem-form-conditionals . 我正在尝试使用react-jsonschema-form和react-jsonschem-form-conditionals从JSON模式构建带有条件字段的表单 。
The components I'm rendering are a FormWithConditionals
and a FormModelInspector
. 我正在渲染的组件是
FormWithConditionals
和FormModelInspector
。 The latter is a very simple component that shows the form model. 后者是一个非常简单的组件,显示了表单模型。
The relevant source code is: 相关的源代码是:
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
};
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 schema = {
type: "object",
title: "User form",
properties: {
nameHider: {
type: 'boolean',
title: 'Hide name'
},
name: {
type: 'string',
title: 'Name'
}
}
};
const uiSchema = {};
const rules = [{
conditions: {
nameHider: {is: true}
},
event: {
type: "remove",
params: {
field: "name"
}
}
}];
const FormWithConditionals = applyRules(schema, uiSchema, rules, Engine)(Form);
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: []
};
However, every time I change a field's value, the field loses focus. 但是,每次更改字段的值时,该字段都会失去焦点。 I suspect the cause of the problem is something in the
react-jsonschema-form-conditionals
library, because if I replace <FormWithConditionals>
with <Form>
, the problem does not occur. 我怀疑问题的原因是
react-jsonschema-form-conditionals
库中的问题,因为如果我用<Form>
替换<FormWithConditionals>
,则不会发生问题。
If I remove the handler onChange={this.handleFormDataChange}
the input field no longer loses focus when it's value changes (but removing this handler breaks the FormModelInspector
). 如果我删除处理程序
onChange={this.handleFormDataChange}
,输入字段在值改变时不再失去焦点(但删除此处理程序会破坏FormModelInspector
)。
In the code above, if I remove the handler onChange={this.handleFormDataChange}
, the <FormModelInspector>
is not updated when the form data changes. 在上面的代码中,如果我删除处理程序
onChange={this.handleFormDataChange}
,则表单数据更改时不会更新<FormModelInspector>
。 I don't understand why this handler is necessary because the <FormModelInspector>
is passed a reference to the form data via the formData
attribute. 我不明白为什么这个处理程序是必要的,因为
<FormModelInspector>
通过formData
属性传递对表单数据的formData
。 Perhaps it's because every change to the form data causes a new object to be constructed, rather than a modification of the same object? 也许是因为对表单数据的每次更改都会导致构造一个新对象,而不是修改同一个对象?
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. 问题非常简单,你在render方法和
onChange
处理程序中创建一个FormWithConditionals
组件,你触发重新渲染的setState
,因此创建了一个新的FormWithConditionals
实例,因此它失去了焦点。 You need to move this instance out of render method and perhaps out of the component itself since it uses static values. 您需要将此实例移出render方法,并且可能需要移出组件本身,因为它使用静态值。
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 作为
schema
, uiSchema
和rules
作为道具传递给ConditionalForm
,您可以在constructor
函数中创建FormWithConditionals
的实例并在渲染中使用它
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 : 对于遇到同样问题但是使用Hooks的人来说,这里没有一个类:
Just use a variable declared outside the component and initialize it inside useEffect
. 只需使用在组件外声明的变量,并在
useEffect
初始化它。 (don't forget to pass []
as second parameter to tell react that we do not depend on any variable, replicating the componentWillMount
effect) (不要忘记传递
[]
作为第二个参数告诉反应我们不依赖任何变量,复制componentWillMount
效果)
// 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
Have you tried declaring function FormModelInspector
as an arrow func : 您是否尝试将
function FormModelInspector
声明为箭头函数:
const FormModelInspector = props => (
<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>
)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.