React(7)-表单

React组件的核心理念就是可预知性和可测试性,给定同样的props和state,任何react组件都会渲染一样的结果。

表单状态属性

表单元素有这么几种属于状态的属性:

  • value,对应 <input><textarea> 所有
  • checked,对应类型为 checkboxradio<input> 所有

在 HTML 中 <textarea> 的值可以由子节点(文本)赋值,但是在 React 中,要用 value 来设置。

表单元素包含以上任意一种状态属性都支持 onChange 事件监听状态值的更改。

针对这些状态属性不同的处理策略,表单元素在 React 里面有两种表现形式:受控组件和非受控组件;

非受控组件

和受控组件相对,如果表单元素没有设置自己的“状态属性”,或者属性值设置为 null,这时候就是非受控组件。

它的表现就符合普通的表单元素,正常响应用户的操作。

同样,你也可以绑定 onChange 事件处理交互。

如果你想要给“状态属性”设置默认值,

  • 文本框和select使用defaultValue
  • 单选框和复选框使用defaultChecked

如果要访问input的值需要设置ref属性即可。

var FormNo = React.createClass({
   submitHandler:funcion(){
       event.preventDefault();
       var Hello = this.refs.Hello.getDOMNode().value;
       console.log(Hello)
   },
   render:function(){
       return(
         <form onSubmit={this.submitHandler}>
            <input ref="Hello" type="text" defaultValue = " hello,xxx"/>
             <button type="submit">提交</button>
         </form>
       )
   }
})

对于select,在 HTML 中 <select> 标签指定选中项都是通过对应 <option>selected 属性来做的,但是在 React 修改成统一使用 value

<select value="B">
    <option value="A">Apple</option>
    <option value="B">Banana</option>
    <option value="C">Cranberry</option>
</select>

你可以通过传递一个数组指定多个选中项:<select multiple={true} value={['B', 'C']}>

受控组件

一个受控的表单组件,它所有状态属性更改涉及 UI 的变更都由 React 来控制(状态属性绑定 UI)。输入的值一般都是由父组件控制的。

如果你希望输入的内容反馈到输入框,就要用 onChange 事件改变状态属性 value 的值:

getInitialState: function() {
    return { value: 'hello'};
},
handleChange: function(event) {
    //所有合成事件都提供了event.target来访问触发事件的DOM节点;例如
    //var DomNode = event.target;
    this.setState({value: event.target.value});
},
render: function() {
    return (
       <label htmlFor="name">Name:</label>
       <input type="text" id="name" value={this.state.value}  onChange={this.handleChange} />;
   )
}

使用这种模式非常容易实现类似对用户输入的验证,或者对用户交互做额外的处理,比如截断最多输入140个字符:

handleChange: function(event) {
    this.setState({value: event.target.value.substr(0, 140)});
}

对于受控组件中的select,获取select值的方法有两种:

  • 通过表单合成事件(即自定义事件,驼峰命名法)event.target.value获取
    handleChange:function(event){
        this.setState({value:event.target.value})
    }
  • 循环检查DOM
    handleChange:function(event){
        var Checked = [];
        for(var i=0;i<event.target.length;i++){
            var option = event.target.options[i];
            if(option.selected){
                Checked.push(option)
            }
        }
        this.setState({options:Checked })
    }

关于表单中的name属性,对于非受控组件建议加上name,对于受控组件加或者不加都行;

多表单元素与change处理器:可以结合Mixin使用

  1. 使用.bind传递参数
  2. 使用DOMNode获取name属性值

这会在后面的自定义表单中举例说明;

Focus

React实现了autoFocus属性,在第一次挂载时通过设置该属性为true即可自动获取焦点。

表单可用性的原则

  • 通过label或者placeholder把输入内容表达清楚
  • 在失去焦点onblur,验证输入信息,不断反馈
  • 使用过渡动画
  • 要么遵循用户习惯,要么从根本上改变用户界面
  • 可访问性:使用一种输入设备
  • 减少用户输入:比如自动补全功能

自定义表单

栗子1:对于checkbox设置的state是对象时,这里使用同时使用name举例:

<script type="text/babel">
      //对于checkbox设置的state是对象时,这里使用同时使用name举例:
      var FromMixin = {
         handleChange:function(text){
            var newState ={};
            var comonents = this;
            return function(event){
                    if(text == 'Game'){
                            newState[text] = comonents.state[text] || {};
                            newState[text][event.target.id] = event.target.checked;
                    }else{
                            newState[text] = event.target.value;
                    };
                    comonents.setState(newState);
            }
         }
      };
      var FormReat = React.createClass({
              mixins:[FromMixin],
              getInitialState:function(){
                return {
                    username:'',
                    password:'',
                    Game:{
                       LOL:false,
                       Dota:false,
                       WOW:false,
                       DNF:false
                    },
                    gender:'man',
                    city:'',
                    textarea:''
                }
              },
              handleSumbit:function(event){
                  event.preventDefault();
                  event.stopPropagation();
                  console.log(this.state)
              },
              render:function(){
                 var divLabel = ['LOL','Dota','WOW','DNF'].map((item,i) => {
                      return (<div key={item}>
                      <label>{item}:</label>
                    <input type="checkbox" name="Game" 
                           id={item}
                        checked={this.state.Game[{item}]} 
                        onChange={this.handleChange('Game')} />
                    </div>)
                  }.bind(this))
                return (
                <form onSubmit={this.handleSumbit}>
                            <div>
                                <label>用户名:</label>
                                <input type="text" name="username" id="username" onChange={this.handleChange('username')} 
                                    value={this.state.name}/>
                            </div>
                            <br/>
                            <div>
                                <label>密  码:</label>
                                <input type="password" name="password" id="password" onChange={this.handleChange('password')} 
                                    value={this.state.password}/>
                            </div>
                            <br/>
                            <div>
                                <h4>性别</h4>
                                <label>男:</label>
                                <input type="radio" name="gender" id="man" value="man" checked={this.state.gender == "man" ? true : false} 
                                    onChange={this.handleChange('gender')}/> 
                                
                                <label>女:</label>
                                <input type="radio" name="gender" id="woman" value="woman" checked={this.state.gender == "woman" ? true : false} 
                                    onChange={this.handleChange('gender')}/>
                            </div>
                            <br/>
                            <div>
                                <label>城市:</label>
                                <select name="city" id="city" onChange={this.handleChange('city')}>
                                    <option value="">请选择</option>
                                    <option value="1">北京</option>
                                    <option value="2">上海</option>
                                    <option value="3">广州</option>
                                </select>
                            </div>
                            <br/>
                            <div>
                                <label>个人简介:</label>
                                <br/>
                                <textarea cols="40" rows="8" name="textarea" id="textarea" onChange={this.handleChange('textarea')}></textarea>
                            </div>
                            <br/>
                            <div>
                                <h4>喜欢的游戏</h4>
                                {divLabel}
                            </div>
                            <br/>
                            <div>
                                <input type="submit" value="Submit" />
                            </div>
                </form>)    
                }
          });
          
        ReactDOM.render(<FormReat />, document.getElementById('app'));
    </script>

栗子2:对于checkbox设置的state是数组时,这里使用同时使用.bind举例:

<script type="text/babel">
     //对于checkbox设置的state是数组时,这里使用同时使用.bind举例:
      var FormReat = React.createClass({
              getInitialState:function(){
                return {
                    username:'',
                    password:'',
                    Game:[],
                    gender:'man',
                    city:'',
                    textarea:''
                }
              },
              handleChange:function(text,event){
                var newState = {};
                if(text == "Game"){
                    newState[text] = this.state.Game || [];
                    var d = newState[text].indexOf(event.target.id);
                    if(event.target.checked){
                        newState[text].push(event.target.id);
                       }else if(d != -1){
                        newState[text].splice(d,1)
                    };
                }else{
                    newState[text] = event.target.value;
                }
                this.setState(newState);
             },
              handleSumbit:function(event){
                  event.preventDefault();
                  event.stopPropagation();
                  console.log(this.state)
              },
              render:function(){
                return (<form onSubmit={this.handleSumbit}>
                            <div>
                                <label>用户名:</label>
                                <input type="text" name="username" id="username" onChange={this.handleChange.bind(this,'username')} 
                                    value={this.state.name}/>
                            </div>
                            <br/>
                            <div>
                                <label>密  码:</label>
                                <input type="password" name="password" id="password" onChange={this.handleChange.bind(this,'password')} 
                                    value={this.state.password}/>
                            </div>
                            <br/>
                            <div>
                                <h4>性别</h4>
                                <label>男:</label>
                                <input type="radio" name="gender" id="man" value="man" checked={this.state.gender == "man" ? true : false} 
                                    onChange={this.handleChange.bind(this,'gender')}/> 
                                
                                <label>女:</label>
                                <input type="radio" name="gender" id="woman" value="woman" checked={this.state.gender == "woman" ? true : false} 
                                    onChange={this.handleChange.bind(this,'gender')}/>
                            </div>
                            <br/>
                            <div>
                                <label>城市:</label>
                                <select name="city" id="city" onChange={this.handleChange.bind(this,'city')}>
                                    <option value="">请选择</option>
                                    <option value="1">北京</option>
                                    <option value="2">上海</option>
                                    <option value="3">广州</option>
                                </select>
                            </div>
                            <br/>
                            <div>
                                <label>个人简介:</label>
                                <br/>
                                <textarea cols="40" rows="8" name="textarea" id="textarea" onChange={this.handleChange.bind(this,'textarea')}></textarea>
                            </div>
                            <br/>
                            <div>
                                <h4>喜欢的游戏</h4>
                                <label>LOL:</label>
                                <input type="checkbox" id="LOL" checked={this.state.Game.indexOf('LOL') < 0 ? false : true} onChange={this.handleChange.bind(this,'Game')}/>
            
                                <label>Dota:</label>
                                <input type="checkbox" id="Dota" checked={this.state.Game.indexOf('Dota') < 0 ? false : true} onChange={this.handleChange.bind(this,'Game')}/>
            
                                <label>WOW:</label>
                                <input type="checkbox" id="WOW" checked={this.state.Game.indexOf('WOW') < 0 ? false : true} onChange={this.handleChange.bind(this,'Game')}/>
                                
                                <label>DNF:</label>
                                <input type="checkbox" id="DNF" checked={this.state.Game.indexOf('DNF') < 0 ? false : true} onChange={this.handleChange.bind(this,'Game')}/>
                       
                            </div>
                            <br/>
                            <div>
                                <input type="submit" value="Submit" />
                            </div>
                           </form>)    
                    }
          });
          
        ReactDOM.render(<FormReat />, document.getElementById('app'));
    </script>

栗子3:利用React.addons.LinkedStateMixin组件

<script src="../JS/react-0.14.7/build/react.js" type="text/javascript" charset="utf-8"></script>
<script src="../JS/react-0.14.7/build/react-dom.js" type="text/javascript" charset="utf-8"></script>
<script src="../JS/react-0.14.7/build/react-with-addons.js" type="text/javascript" charset="utf-8"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-core/5.8.24/browser.min.js"></script>
<script type="text/babel">
      var FormReat = React.createClass({
              mixins:[React.addons.LinkedStateMixin],
              getInitialState:function(){
                return {
                    username:'',
                    password:'',
                    Game:[],
                    gender:'man',
                    city:'',
                    textarea:''
                }
              },
              handleChange:function(text,event){
                var newState = {};
                if(text == "Game"){
                    newState[text] = this.state.Game || [];
                    var d = newState[text].indexOf(event.target.id);
                    if(event.target.checked){
                        newState[text].push(event.target.id);
                       }else if(d != -1){
                        newState[text].splice(d,1)
                    };
                }else{
                    newState[text] = event.target.value;
                }
                this.setState(newState);
             },
              handleSumbit:function(event){
                  event.preventDefault();
                  event.stopPropagation();
                  console.log(this.state)
              },
            render:function(){
                return (<form onSubmit={this.handleSumbit}>
                            <div>
                                <label>用户名:</label>
                                <input type="text" name="username" id="username" valueLink = {this.linkState('username')}/>
                            </div>
                            <br/>
                            <div>
                                <label>密  码:</label>
                                <input type="password" name="password" id="password" valueLink = {this.linkState('password')}/>
                            </div>
                            <br/>
                            <div>
                                <h4>性别</h4>
                                <label>男:</label>
                                <input type="radio" name="gender" id="man" value="man" checked={this.state.gender == "man" ? true : false} 
                                    onChange={this.handleChange.bind(this,'gender')}/> 
                                
                                <label>女:</label>
                                <input type="radio" name="gender" id="woman" value="woman" checked={this.state.gender == "woman" ? true : false} 
                                    onChange={this.handleChange.bind(this,'gender')}/>
                            </div>
                            <br/>
                            <div>
                                <label>城市:</label>
                                <select name="city" id="city" valueLink = {this.linkState('city')}>
                                    <option value="">请选择</option>
                                    <option value="1">北京</option>
                                    <option value="2">上海</option>
                                    <option value="3">广州</option>
                                </select>
                            </div>
                            <br/>
                            <div>
                                <label>个人简介:</label>
                                <br/>
                                <textarea cols="40" rows="8" name="textarea" id="textarea"  valueLink = {this.linkState('textarea')}></textarea>
                            </div>
                            <br/>
                            <div>
                                <h4>喜欢的游戏</h4>
                                <label>LOL:</label>
                                <input type="checkbox" id="LOL" checked={this.state.Game.indexOf('LOL') < 0 ? false : true} onChange={this.handleChange.bind(this,'Game')}/>
            
                                <label>Dota:</label>
                                <input type="checkbox" id="Dota" checked={this.state.Game.indexOf('Dota') < 0 ? false : true} onChange={this.handleChange.bind(this,'Game')}/>
            
                                <label>WOW:</label>
                                <input type="checkbox" id="WOW" checked={this.state.Game.indexOf('WOW') < 0 ? false : true} onChange={this.handleChange.bind(this,'Game')}/>
                                
                                <label>DNF:</label>
                                <input type="checkbox" id="DNF" checked={this.state.Game.indexOf('DNF') < 0 ? false : true} onChange={this.handleChange.bind(this,'Game')}/>
                       
                            </div>
                            <br/>
                            <div>
                                <input type="submit" value="Submit" />
                            </div>
                           </form>)    
                    }
          });
          
        ReactDOM.render(<FormReat />, document.getElementById('app'));
    </script>