React 在内心深处获取价值 DOM

React get value deep inside DOM

我对 React 不太熟悉,我尝试在登录/表单等方面构建以下抽象。

看看这个:

    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 class 的值(只需获取表单值)?我的输入标签在 DOM 深处有问题。附加问题是否有以下代码设计我的意思是将每个逻辑组件描述为新的 'class' ?

关于你的第二个问题 - 为每个逻辑元素添加一个组件是一种糟糕的方法。 在您的示例中 - 所有组件(除了第一个)都没有逻辑,只有 '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 并且喜欢它)

在没有像 Angular or Flux 这样的框架的情况下,使用回调在组件之间发送数据。

这里使用的技术是explained in detail on React's website. Another solution, however, is to use a form library. I took a similar route and tried to build form components from scratch -- it works, but it's a path others have already cleared. Check out newforms and react-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. <input/>改变时,它运行handleChange
  2. handleChange运行时,它将FormGroup的fieldNamevalue传递给SignUpForm
  3. SignUpForm 使用新字段值更新其状态

我建议使用 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 对象中的值。

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

我最近拆分了代码 newforms uses for this out into a reusable 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>
  }
});