【问题标题】:React get value deep inside DOMReact 在 DOM 内部获取价值
【发布时间】:2015-04-02 19:53:39
【问题描述】:

我对反应不是很熟悉,我尝试在登录/表单等上构建以下抽象。

看看这个:

    var SignUpForm = React.createClass({

    handleSubmit: function(e) {
        e.preventDefault();
        console.log(this.refs.iitu_id.getDOMNode().value.trim())

        // iitu_id = this.refs.iitu_id.getDOMNode().value.trim();
        // password = this.refs.password.getDOMNode().value.trim();

        var error = UserValidator.valid({iitu_id: iitu_id, password: password});
        if (error) {
            this.setState({"errors": error });
            // console.log(error);
        } else {
            // console.log(error);
        }

    },

    getInitialState: function() {
        return {
            'errors': {
                iitu_id: null, 
                password: null
            }
        };
    },

    render: function() {
        return (
            /*jshint ignore:start */
            <form className="form-horizontal" onSubmit={this.handleSubmit} >
                <FormGroup label="iitu id" error_msg={this.state.errors.iitu_id} fieldName="iitu_id" fieldType="text" />
                <FormGroup label="password" error_msg={this.state.errors.password} fieldName="password" fieldType="password" />
                <ButtonGroup text="Войти"/>
            </form>
            /*jshint ignore:end */
        );
    }
});

var FormGroup = React.createClass({

    render: function() {
        var formGroupCss = 'form-group';
        if (this.props.error_msg){
            formGroupCss = 'form-group has-error';
        }

        return (
            /*jshint ignore:start */
            <div className={formGroupCss}>
                <Label fieldName={this.props.fieldName}>{this.props.label}</Label>
                <InputField type={this.props.fieldType}>{this.props.label}</InputField>
                <label className="control-label" for="inputError1">{this.props.error_msg}</label>
            </div>
            /*jshint ignore:end */
        );
    }
});



var ButtonGroup = React.createClass({
    render: function () {
        return (
            /*jshint ignore:start */
            <div className="form-group">
                <div className="col-sm-offset-2 col-sm-10">
                    <button className="btn btn-default">{this.props.text}</button>
                </div>
            </div>
            /*jshint ignore:end */
        );
    }
});

var InputField = React.createClass({
    render: function() {

        return (
            /*jshint ignore:start */
            <div className="col-sm-5">
                <input className="form-control" type={this.props.fieldType} placeholder={this.props.children}/>
            </div>
            /*jshint ignore:end */
        );
    }
});

exports.SignUpForm = SignUpForm;

代码有点多,但我认为它很容易阅读。问题是当我按下提交按钮时如何获取 InputField 类的值(只需获取表单值)?我的输入标签在 DOM 深处存在问题。额外的问题是有以下代码设计好不好?我的意思是将每个逻辑组件描述为新的“类”?

【问题讨论】:

  • 您只能在给定组件内使用 ref 访问 dom 节点。您正在尝试使用它来遍历几个级别的子组件。您可以尝试为每个子 example in this question 传递 ref 属性或使用您喜欢的框架(例如 Backbone 或 Flux)中的模型层。我会尝试将少数组件组合成只有 1 级子组件,并避免通过模型传递数据。

标签: javascript dom reactjs


【解决方案1】:

如果您为表单输入提供name 属性,则可以使用表单的.elements 集合来访问其输入。

我最近将 newforms 用于此的代码拆分为可重用的 get-form-data 模块,假设您的输入具有 name 属性,您可以这样做:

var getFormData = require('get-form-data'); // Or just use the browser bundle

var SignUpForm = React.createClass({
  handleSubmit: function(e) {
    e.preventDefault();
    var data = getFormData(e.target, {trim: true});
    var error = UserValidator.valid(data);
    if (error) {
      this.setState({errors: error});
    } else {
      // ...
    }
  },
  // ...

或者,如果您需要获取输入,您可以将onChange 处理程序添加到&lt;form&gt; 或包含所有表单输入的其他组件,而不必单独执行每个输入:

  handleChange: function(e) {
    var form = e.target.form;
    var name = e.target.name;
    var data = getFormData.getNamedFormElementData(form, name, {trim: true});
    // ...
  },

  render: function() {
    return <form onSubmit={this.handleSubmit} onChange={this.handleChange}>
      {/* ... */}
    </form>
  }
});

【讨论】:

    【解决方案2】:

    关于您的第二个问题 - 为每个逻辑元素添加一个组件是一种不好的方法。 在您的示例中-所有组件(除了第一个组件)都没有逻辑,只有“渲染”。这种设计增加了许多无用的代码行和“this.props”重复。 在您的示例中,我只需将所有内容添加到第一个组件的渲染中。

    现在,假设您有两个组件,并希望从父级访问子级:

    var SignUpForm = React.createClass({
    
      handleSubmit: function(e) {
        e.preventDefault();
    
        //now - reach your inner comp with refs
        console.log(this.refs.inner.refs.username.getDOMNode().value.trim());      
      },
    
      render: function() {
        return (
           //and pass the submit function as a callback
           <InnerForm ref="inner" submit={this.props.handleSubmit}/>           
        );
      }
    });
    
    var InnerForm = React.createClass({
    
      render: function() {
        return (
           <form>  
               <input type="text" ref="username"/>
               <input type="submit" onClick={this.props.submit} >
           </form>           
        );
      }
    });
    

    在您的示例中,组件中有很多组件可以执行我所做的事情。 不过,在某些情况下,您需要长联的子组件。使用 this.refs.a.refs.b.refs.c 等 - 可能会非常难看,并且还会阻止可重用性。

    在这些情况下尝试使用任何 MVC 架构(我正在使用 reflux 并喜欢它)

    【讨论】:

    • 感谢您的回答,但我可以就一件事与您争论。在我看来,在我的代码中,作为不同类的“错误块”实际上是个好主意,以及可以重用的逻辑组件,我的意思是你传递字段和错误消息,它会显示它,关于你是对的一切。
    • 我创建 FormGroup 类的原因是,我不想在每次有表单时都编写
      样板。有没有办法通过反应跳过它?
    • 我认为编写所有内容将有助于提高可用性,但您可以这样做:render: function(){ var inputForm = &lt;div&gt;boilerplate content&lt;/div&gt; return(&lt;div&gt;{inputForm}&lt;/div&gt;)}。如果您想在其他组件中重用它,请将其放在 mixin 中,并引用 var inputForm = this.inputForm。虽然我还没有看到有人使用这种设计。
    • 如果您想添加一些功能(例如示例中的“道具”),请创建一个函数:render: function(){ var inputForm = function(type){&lt;input type={type}/&gt;} return(&lt;div&gt;{inputForm(text)}&lt;/div&gt;)}。我相信这应该可行...
    • 关于验证器 - 尝试在 mixin 中使用它
    【解决方案3】:

    在没有像 AngularFlux 这样的框架的情况下,使用回调在组件之间拍摄数据。

    这里使用的技术是 React 网站上的 explained in detail。然而,另一种解决方案是使用表单库。我采取了类似的路线并尝试从头开始构建表单组件——它有效,但这是其他人已经清除的路径。查看newformsreact-forms

    请注意,我分享的代码未经测试,但应该非常接近工作。

    var SignUpForm = React.createClass({
    
        handleSubmit: function(e) {
            e.preventDefault();
    
            console.log(this.iitu_id + ' and ' + this.password + ' should work.');
    
            var error = UserValidator.valid({iitu_id: iitu_id, password: password});
            if (error) {
                this.setState({"errors": error });
                // console.log(error);
            } else {
                // console.log(error);
            }
    
        },
    
        getInitialState: function() {
            return {
                'errors': {
                    iitu_id: null, 
                    password: null
                }
            };
        },
    
        updateForm: function(field, value) {
            /*  Or do something like this using underscore.js:
    
                    var form = _.clone(this.form);
                    form[field] = value;
                    this.setState({ form: form });
    
                This approach let's you grab the form object on
                submit instead of hunting for the form values
                in SignUpForm's state.
            */
    
            this.setState({ field: value });
        },
    
        render: function() {
            return (
                /*jshint ignore:start */
                <form className="form-horizontal" onSubmit={this.handleSubmit} >
                    <FormGroup update={this.updateForm} label="iitu id" error_msg={this.state.errors.iitu_id} fieldName="iitu_id" fieldType="text" />
                    <FormGroup label="password" error_msg={this.state.errors.password} fieldName="password" fieldType="password" />
                    <ButtonGroup text="Войти"/>
                </form>
                /*jshint ignore:end */
            );
        }
    });
    
    var FormGroup = React.createClass({
    
        handleChange: function(event) {
            this.props.update(this.props.fieldName, event.target.value);
        },
    
        render: function() {
            var formGroupCss = 'form-group';
            if (this.props.error_msg){
                formGroupCss = 'form-group has-error';
            }
    
            return (
                /*jshint ignore:start */
                <div className={formGroupCss}>
                    <Label fieldName={this.props.fieldName}>{this.props.label}</Label>
                    <InputField handleChange={this.handleChange} type={this.props.fieldType}>{this.props.label}</InputField>
                    <label className="control-label" for="inputError1">{this.props.error_msg}</label>
                </div>
                /*jshint ignore:end */
            );
        }
    });
    
    
    var ButtonGroup = React.createClass({
        render: function () {
            return (
                /*jshint ignore:start */
                <div className="form-group">
                    <div className="col-sm-offset-2 col-sm-10">
                        <button className="btn btn-default">{this.props.text}</button>
                    </div>
                </div>
                /*jshint ignore:end */
            );
        }
    });
    
    var InputField = React.createClass({
        render: function() {
    
            return (
                /*jshint ignore:start */
                <div className="col-sm-5">
                    <input onChange={this.props.handleChange} className="form-control" type={this.props.fieldType} placeholder={this.props.children}/>
                </div>
                /*jshint ignore:end */
            );
        }
    });
    
    exports.SignUpForm = SignUpForm;
    

    具体来说,请注意从父级传递给子级的回调函数。

    1. SignUpForm 将 updateForm 提供给 FormGroup
    2. FormGroup 将handleChange 提供给 InputField

    然后要将数据返回到顶部,您只需反转该过程即可。

    1. &lt;input/&gt; 改变时,它运行handleChange
    2. handleChange 运行时,它会将FormGroup 的fieldNamevalue 传递给SignUpForm
    3. SignUpForm 使用新字段值更新其状态

    【讨论】:

      【解决方案4】:

      我建议为您的输入使用 onChange 事件侦听器,该事件侦听器使用回调将信息一直传递回您的顶级组件。

      var SignUpForm = React.createClass({
        getInitialState: function() {
          return {
            inputs: {
              id: '',
              password: ''
            }
          };
        },
        render: function() {
          return (
            <form className="form-horizontal" onSubmit={this.handleSubmit} >
              <FormGroup ... callback={this.handleIdChange} />
              <FormGroup ... callback={this.handlePasswordChange} />
              <ButtonGroup text="Войти"/>
            </form>
          );
        },
        handleIdChange: function(newId) {
          this.setState({
            inputs: {
              id: newId.target.value,
              password: this.state.inputs.password
            }
          });
        },
        handlePasswordChange: function(newPassword) { ... }
      });
      

      然后将该回调传递给基本级别的表单字段,并使用它们来处理onChange 事件。

      var InputField = React.createClass({
        render: function() {
      
          return (
            /*jshint ignore:start */
            <div className="col-sm-5">
              <input ... onChange={this.props.callback}/>
            </div>
            /*jshint ignore:end */
          );
        }
      });
      

      然后用你的handleSubmit 输出this.state.inputs 对象中的值。

      【讨论】:

        猜你喜欢
        相关资源
        最近更新 更多
        热门标签