简体   繁体   English

输入值更改时,表单字段会失去焦点

[英]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-formreact-jsonschem-form-conditionals从JSON模式构建带有条件字段的表单

The components I'm rendering are a FormWithConditionals and a FormModelInspector . 我正在渲染的组件是FormWithConditionalsFormModelInspector The latter is a very simple component that shows the form model. 后者是一个非常简单的组件,显示了表单模型。

屏幕截图2018-02-01 at 17 50 32

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 )。

Aside 在旁边

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 作为schemauiSchemarules作为道具传递给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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM