1. 程式人生 > >【09】react 之 表單組件

【09】react 之 表單組件

密碼 愛好 你在 false create 保留 input 數據 編寫

不太清楚有多少初學React的同學和博主當時一樣,在看完React的生命周期、數據流之後覺得已經上手了,甩開文檔啪啪啪的開始敲了起來。結果...居然被一個input標簽給教做人了。

技術分享

故事是這樣的:首先你創建了一個input標簽

技術分享
var React = require(‘react‘),
    ReactDOM = require(‘react-dom‘);

var Test = React.render(function() {
    render: function() {
        return (<input type="text" />);
    }
});

ReactDOM.render(<Text />, document.querySelector(‘#container‘));
技術分享

一切都是如此的輕松自然,接著由於需求你給input上設置了一個默認值:

<input value=‘123‘ type=‘text‘ />

突然你發現,唉我擦!輸入框裏的值不能改動了,刪也刪不掉。你以為電腦卡死了,刷新了幾遍還是這樣。然而把value刪除就復原了,你不得不又返回去看文檔。

原理:在React中表單組件分為約束組件和無約束組件兩種。

  - 無約束組件,是指其value值不通過的props或者state來設置,僅由其自身來決定。表單組件的值的變化也不會被記錄,只能通過找到DOM節點的方式來獲取。

  - 約束組件,是React中推薦的表單的使用方式。表單組件的值並不是由其自身決定,而是通過父組件傳遞或者本身的state來控制。其內容的每次變化都會被保存,需要時僅需要通過this.state便能獲取。

約束狀態的input組件寫法如下:

 class Test extends React.Component{
    constructor(props){
        super(props);
        this.handerChange = this.handerChange.bind(this);
    }
    handerChange(event){
        let newVal = event.target.value;
        console.log(newVal);
        this.setState({
            text:newVal
        });
    }
    render(){
        return (
            <input type="text" value={this.props.text} onChange={this.handerChange}/>
        );
    }
 }
  ReactDOM.render(<Test/>,document.getElementById(‘example‘))

上例中,我們監聽了input的onchange事件,每一次內容的更改實際上是更改組件的state屬性,通過state的變化來觸發DOM元素的變化。

React之所以這麽做的原因,是因為React其實為一個狀態機,頁面上所有的DOM元素的狀態都需要被其所知所控制。

在繼續理解表單組件之前,組件的state是必須被開發者所理解的。通常很多人喜歡將state與props一起講解,這裏博主認為通過state在表單組件的實際應用講解可能更加直觀。

State

每一本介紹React的書或文檔都會把state和props放在一起詳細的比較,其實最簡單的說:state是組件內部用來控制組件狀態的屬性,props是組件之間用來通信的屬性。

創建

state是通過名為getInitialState的生命周期函數創建的,其return出一個對象作為state值。如果你申明了該函數卻沒有返回值是會報錯的。

創建之後,在組件內部的所有函數都可以用 this.state.屬性名 來訪問該屬性。

修改

state的值並不是固定的,開發都通過在合適的時機改變它從而達到改變頁面展示的目的。

改變state的唯一是this.setState,該方法可以說是整個React系統的"扳機",正常情況(除了直接操作DOM)下所有的頁面更新都是由這個方法來觸發的。

 class Test extends React.Component{
    constructor(props){
        super(props);
        this.state = {
            text: "init text",
            }
        this.handerChange = this.handerChange.bind(this);
    }
   
    handerChange(event){
        let newVal = event.target.value;
        this.setState({
            text:newVal
        });
    }
    render(){
        return (
            <input type="text" value={this.state.text} onChange={this.handerChange}/>
        );
    }
 }
  ReactDOM.render(<Test/>,document.getElementById(‘example‘))

這裏就是有關state很重要的一點:絕對不要直接更改state的值,只通過setState來改變。否則會因為多個地方多次對state更改,導致不統一。從而引發一些不必要的問題。

當state裏有多個屬性,如果需要更新某一個組件不用更新state裏所有的屬性,只更新需要的就好:

{name: ‘lilei‘, age: 25, sex: ‘男‘} //state

this.setState({name: ‘hanmeimei‘, sex: ‘女‘}); //state:{name: ‘hanmeimei‘, age: 25, sex: ‘女‘}

更新時機

既然setState是React的扳機,那它就不能隨便在哪裏都開槍。可能這部分東西需要對React的生命周期有一定掌握,許多文檔和博客裏都寫得很詳細。我這裏就不再抄書了。

通常調用setState都是在人工觸發的事件裏,比如上例中的handlerClick。但總有需要自動觸發的情形。生命周期主要分為創建、更新和銷毀三個階段。

  - 首先,在任何階段的render函數裏都是不可以調用setState來觸發更新的。

  - 創建階段,一般是在componentWillMount以及componentDidMount這兩個生命周期函數中調用,前者表示React即將渲染真實DOM前的一個階段,也是最後的修改state的機會。後者表示真實DOM已經渲染完成,在頁面中能看到我們的組件了,這裏再調用setState就會觸發組件的一次更新。在實際開發中通常用在下面這種情況:(下面是es5的寫法)

技術分享
var Foo = React.createClass({
  getInitialState:function() {
    return {....};
  },
  render: function() {
    return (<..../>);
  },
  componentDIdMount: function(){
    AJAX {
      this.setState({....});
    }   } });
技術分享

大意就是首先創建出頁面元素,在componentDidMount函數中發起ajax之類的請求,獲取數據後通過setState更新頁面將數據更新到頁面中。

這樣做的好處就是在請求較慢或者請求失敗的情況下,頁面不至於留白,影響用戶體驗。

  - 更新階段,絕...對...不...要...在...這...個...階...段...調...用。因為如果在該階段任意一個生命周期函數中使用setState觸發頁面更新時,組件又會再次進入生命周期的更新階段,這裏會再次調用setState方法,然後進入死循環。

  - 銷毀階段,就更不用說了,組件都沒得了,還更新個毛啊。

了解完了state,繼續看input的無約束組件。

<input defaultValue="123" />

如果設置了defaultValue屬性,該組件就是無約束組件。此時可以直接設置input的默認值,設置之後內容可以直接進行更改。缺點是這個屬性貌似只能設置一次,重復設置無效。

如果你不想為約束組件編寫如上那些繁瑣的過程,React提供了簡單的方法——mixin。

mixin

簡單來說mixin是用來抽象某一功能的工具,將邏輯抽象出來,使其可以在多個組件裏復用。除了自定義以外,官方已經封裝了一系列的mixin組件,使用前需要引入react-with-addons文件。

技術分享
var Form = React.createClass({
  mixins: [React.addons.LinkedStateMixin],
  getInitialState: function() {
      return {userName: ‘‘, passWord: ‘‘};
  },
  render: function() {
      return (<div>
          <form>
            <input type=‘text‘ valueLink={this.linkState(‘userName‘)} placeholder=‘用戶名‘  />
            <input type=‘password‘ valueLink={this.linkState(‘passWord‘)} placeholder=‘密碼‘ />
          </form>
        </div>);
  }
});
技術分享

如上引入mixin組件後,只需要在input的特殊屬性valueLink中調用this.linkState(‘屬性名‘),之後每次對input內容的更改就會同步到組件state中同名的屬性中。

其實LinkedStateMixin內部的實現跟我們Test那個示例組件裏是一樣的,看懂了那段代碼就能理解這個mixin插件的內部原理了。

*雖然使用mixin可以簡化書寫流程,但是使用這種方式往數據流中添加定制功能時,復雜度會增加,建議只在特定場景下使用。傳統的約束表單組件更加靈活。

下面介紹下其他表單組件的內容

Label

label元素是表單中很重要的一個部分,由於for在JavaScript中是一個保留字,所以在JSX中for屬性更改為htmlFor。

<label htmlFor=‘name‘>姓名</label>

Textarea

與傳統的HTML相比,在React中,textarea被修改為更像input的形式。

<textarea value={this.state.value} />

textarea的約束組件的使用方法與input一致,同時也可以使用同一個mixin。

<textarea valueLink={this.linkState(‘value‘)} />

使用defaultValue屬性同樣可以將textarea變為無約束組件。

<textarea defaultValue=‘請輸入內容‘ />

Select

在React中select與textarea一樣,相比HTML也作了一些修改,使它們操作起來更簡便。

無約束組件:

<select defaultValue=‘B‘>
  <option value=‘A‘>AAA</option>
  <option value=‘B‘>BBB</option>
  <option value=‘C‘>CCC</option>
</select>

約束組件:

技術分享
var SelectComponent= React.createClass({
  getInitialState: function() {
    return {option: ‘A‘};
  },
  render: function() {
    return (<select value={this.state.option} onChange={this.handlerChange}>
          <option value=‘A‘>A</option>
          <option value=‘B‘>B</option>
          <option value=‘C‘>C</option>
        </select>);
  },
  handlerChange: function(event) {
    this.setState({option: event.target.value});
  }
技術分享

單選

約束組件:

技術分享
var Radio = React.createClass({
  getInitialState: function() {
    return {gender: ‘男‘};
  },
    render: function() {
    return (<div>
          <input type=‘radio‘ name=‘gender‘ value=‘男‘ checked={this.state.sex == ‘男‘} onChange={this.handlerChange} />男
          <input type=‘radio‘ name=‘gender‘ value=‘女‘ checked={this.state.sex == ‘女‘} onChange={this.handlerChange} />女
        </div>);   
  },
  handlerChange: function(event) {
    this.setState({gender: event.target.value});   } });
技術分享

設置單選的defaultChecked會使其變為無約束組件。

<input type=‘radio‘ defaultChecked=‘true‘ />

復選

約束組件:

技術分享
var CheckBox = React.createClass({
  getInitialState: function() {
    return {basketBall: false, swim: false, sing: false};
  },
  render: function() {
    return (<div>
          <p>愛好:</p>           <input type=‘checkbox‘ checked={this.state.basketBall} value=‘basketBall‘ onChange={this.handlerChange} />籃球           <input type=‘checkbox‘ checked={this.state.swim} value=‘swim‘ onChange={this.handlerChange} />遊泳
          <input type=‘checkbox‘ checked={this.state.sing} value=‘sing‘ onChange={this.handlerChange} />唱歌         </div>);   },   handlerChange: function(event) {
    var type = event.target.value,
       checked = event.target.checked,
       newState = {};
    newState[type] = checked;
    this.setState(newState);   } });
技術分享

在handlerCheck函數中有一點要註意,我創建了一個中間變量newState。

handlerCheck: function(event) {
  var type = event.target.value,
     checked = event.target.value;
  this.setState(type: checked); //state: {basketBall: false, swim: false, sing: false, type: true} }

如果像上面的寫法,type並不會作為變量,而是作為字符串解析。每當你在setState時遇到困難時,嘗試中間變量,這方法百試不爽。

無約束組件:

<input type=‘checkbox‘ defaultChecked=‘true‘ />

多表單元素與change事件處理

在實際開發中通常有多個表單組件,為了使一個change處理器能處理所有的表單組件變化,可以使用bind方法來綁定類型。

技術分享
var FormComponent = React.createClass({
  getInitialState: function() {
    return {name: ‘‘, gender: ‘男‘};
  },
  render: function() {
    return (<form>
          <input type=‘text‘ value={this.state.name} onChange={this.handlerChange.bind(this,‘name‘)} />
          <label htmlFor=‘male‘>男</label>
          <input id=‘male‘
              name=‘gender‘
              type=‘radio‘
              value=‘男‘
              checked={this.state.gender == ‘男‘}
              onChange={this.handlerChange.bind(this,‘gender‘)} />
          <label htmlFor=‘female‘>女</label>
          <input id=‘female‘
              name=‘gender‘
              type=‘radio‘
              value=‘女‘
              checked={this.state.gender == ‘女‘}
              onChange={this.handlerChange.bind(this,‘gender‘)} />
        </form>);
  },
  handlerChange: function(type, event) {
    var newState = {};
    newState[type] = event.target.value;
    this.setState(newState);
  }
});
技術分享

表單是React初學者很容易踩的大坑,但是對表單組件的學習可以很快的理解state屬性。

轉載:http://www.cnblogs.com/ghost-xyx/p/5253567.html

【09】react 之 表單組件