React在DOM内部深入获取值

时间:2015-02-03 14:58:30

标签: javascript dom reactjs

我对反应并不熟悉,我尝试在登录/表格等方面构建以下抽象。

看看这个:

    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内部的问题。另外一个问题是,拥有以下代码设计是否合适?我的意思是将每个逻辑组件描述为新的“类”?

4 个答案:

答案 0 :(得分:7)

如果您提供表单输入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或包含所有表单输入的其他组件添加<form>处理程序,而不是分别做每一个:

  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>
  }
});

答案 1 :(得分:2)

如果没有像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
  3. 然后,要将数据恢复到顶部,您只需撤消该过程。

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

答案 2 :(得分:1)

关于你的第二个问题 - 为每个逻辑元素添加一个组件是一个糟糕的方法。 在您的示例中 - 所有组件(除第一个之外)都没有逻辑,只有'render'。这种设计增加了许多无用的代码行和“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并喜欢它)

答案 3 :(得分:1)

我建议您输入一个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对象中的值。