输入值更改时表单字段失去焦点
Form fields lose focus when input value changes
我正在尝试使用 react-jsonschema-form and react-jsonschem-form-conditionals.
从 JSON 模式构建一个包含条件字段的表单
我正在渲染的组件是 FormWithConditionals
和 FormModelInspector
。后者是一个非常简单的显示表单模型的组件。
相关源码为:
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: []
};
但是,每次我更改一个字段的值时,该字段都会失去焦点。我怀疑问题的原因是 react-jsonschema-form-conditionals
库中的东西,因为如果我用 <Form>
替换 <FormWithConditionals>
,问题就不会发生。
如果我删除处理程序 onChange={this.handleFormDataChange}
输入字段在其值更改时不再失去焦点(但删除此处理程序会破坏 FormModelInspector
)。
放在一边
在上面的代码中,如果我删除处理程序 onChange={this.handleFormDataChange}
,当表单数据更改时 <FormModelInspector>
不会更新。我不明白为什么需要这个处理程序,因为 <FormModelInspector>
通过 formData
属性传递了对表单数据的引用。也许是因为表单数据的每一次改变都会导致构造一个新的对象,而不是对同一个对象的修改?
问题非常简单,您在渲染方法中创建了一个 FormWithConditionals
组件,在 onChange
处理程序中您 setState
触发了重新渲染,从而触发了一个新的FormWithConditionals
的实例已创建,因此它失去了焦点。您需要将此实例移出 render 方法,或许还移出组件本身,因为它使用静态值。
由于 schema
、uiSchema
和 rules
作为属性传递给 ConditionalForm
,您可以在 [=20] 中创建 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: []
};
您是否尝试过将 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>
)
对于遇到相同问题但使用 Hooks 的任何人,这里是没有 class 的方法:
只需使用在组件外部声明的变量并在内部初始化它 useEffect
。 (不要忘记传递 []
作为第二个参数来告诉 React 我们不依赖于任何变量,复制 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
我正在尝试使用 react-jsonschema-form and react-jsonschem-form-conditionals.
从 JSON 模式构建一个包含条件字段的表单我正在渲染的组件是 FormWithConditionals
和 FormModelInspector
。后者是一个非常简单的显示表单模型的组件。
相关源码为:
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: []
};
但是,每次我更改一个字段的值时,该字段都会失去焦点。我怀疑问题的原因是 react-jsonschema-form-conditionals
库中的东西,因为如果我用 <Form>
替换 <FormWithConditionals>
,问题就不会发生。
如果我删除处理程序 onChange={this.handleFormDataChange}
输入字段在其值更改时不再失去焦点(但删除此处理程序会破坏 FormModelInspector
)。
放在一边
在上面的代码中,如果我删除处理程序 onChange={this.handleFormDataChange}
,当表单数据更改时 <FormModelInspector>
不会更新。我不明白为什么需要这个处理程序,因为 <FormModelInspector>
通过 formData
属性传递了对表单数据的引用。也许是因为表单数据的每一次改变都会导致构造一个新的对象,而不是对同一个对象的修改?
问题非常简单,您在渲染方法中创建了一个 FormWithConditionals
组件,在 onChange
处理程序中您 setState
触发了重新渲染,从而触发了一个新的FormWithConditionals
的实例已创建,因此它失去了焦点。您需要将此实例移出 render 方法,或许还移出组件本身,因为它使用静态值。
由于 schema
、uiSchema
和 rules
作为属性传递给 ConditionalForm
,您可以在 [=20] 中创建 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: []
};
您是否尝试过将 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>
)
对于遇到相同问题但使用 Hooks 的任何人,这里是没有 class 的方法:
只需使用在组件外部声明的变量并在内部初始化它 useEffect
。 (不要忘记传递 []
作为第二个参数来告诉 React 我们不依赖于任何变量,复制 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