【问题标题】:Form fields lose focus when input value changes输入值更改时表单字段失去焦点
【发布时间】:2018-07-12 04:14:54
【问题描述】:

我正在尝试使用 react-jsonschema-formreact-jsonschem-form-conditionals 从 JSON 模式构建一个带有条件字段的表单。

我正在渲染的组件是FormWithConditionalsFormModelInspector。后者是一个非常简单的组件,展示了表单模型。

相关源码为:

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 库中,因为如果我将&lt;FormWithConditionals&gt; 替换为&lt;Form&gt;,则不会出现问题。

如果我删除处理程序 onChange={this.handleFormDataChange},则输入字段在值更改时不再失去焦点(但删除此处理程序会破坏 FormModelInspector)。

一边

在上面的代码中,如果我删除处理程序onChange={this.handleFormDataChange},当表单数据更改时&lt;FormModelInspector&gt; 不会更新。我不明白为什么这个处理程序是必要的,因为&lt;FormModelInspector&gt; 通过formData 属性传递了对表单数据的引用。也许是因为表单数据的每一次更改都会导致构造一个新对象,而不是修改同一个对象?

【问题讨论】:

    标签: forms reactjs jsonschema


    【解决方案1】:

    问题很简单,你在你的渲染方法和你的onChange处理程序中创建了一个FormWithConditionals组件,你setState触发了重新渲染,因此创建了一个FormWithConditionals的新实例,并且因此它失去了焦点。您需要将此实例移出渲染方法,并且可能会移出组件本身,因为它使用静态值。

    由于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: []
        };
    

    【讨论】:

    • 感谢您的回答。明天我会试试你的建议,如果可行,我会接受你的回答。你能解释一下“它使用静态值”是什么意思吗?
    • 应用规则接受其值为简单常量字符串且不依赖于变量的对象
    • 你提出的这个改变——将applyRules移到ConditionalForm组件之外——不起作用,因为applyRulesschemauiSchemarules参数是ConditionalForm 组件。
    • 在你的问题中,你提到它们是不变的吗??
    • schemauiSchemarules 是常量,如果你问的是这个?
    【解决方案2】:

    您是否尝试过将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>
      )
    

    【讨论】:

    • @Dónal 我可能误解了您在“旁白”中所说的内容,但是将此函数声明为无状态组件或 pureComponent 可以防止一些不必要的渲染()。实际上我认为你的问题是你构建 FormWithConditionals 的方式,如果另一个不适合你,我会更新我的答案。
    • FormModelInspector 重新定义为箭头函数并没有改变任何东西
    【解决方案3】:

    对于遇到同样问题但使用 Hooks 的任何人,以下是没有类的方法:

    只需使用在组件外部声明的变量并在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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-10-21
      • 2022-01-19
      • 2023-03-08
      • 1970-01-01
      • 2015-09-21
      • 1970-01-01
      相关资源
      最近更新 更多