react學習筆記 item8 --- 表單

表單是應用必不可少的一部分,只要需要用戶輸入,哪怕是最簡單的輸入,都離不開表單。一直以來,單頁應用中的表單都很難處理好,因爲表單中充斥着用戶變化莫惻的狀態。在 react 中,表單的處理也與原生的 JS 處理方法稍有不同。在 react 中,表單組件有兩種類型:受限組件(約束組件)不受限組件(無約束組件)

受限組件

約束組件的模式與React 其他類型組件的模式一致。表單組件的狀態交由React 組件控制,狀態值被存儲在React 組件的state 中。如果想要更好地控制表單組件,推薦使用約束組件。

先看下面一個例子

// 示例1
var MyForm = React.createClass({
    render:function(){
        return(
            <input type='text' value='defaultText' />
            );
    }
});

ReactDOM.render(
   <MyForm />,
  document.getElementById('example')
);

上面的代碼將渲染出一個值爲 defaultText 的 input 元素。用戶在渲染出來的元素裏輸入任何值都不起作用,因爲 React 已經賦值爲 defaultText。通過運行,我們在 chrome 瀏覽器中查看到如下結果:

這裏寫圖片描述

在控制檯打印出了一條 warning, 提醒我們設置了 value 的時候需要同時提供 onChange 事件,不然表單是隻讀的。因此如果想響應更新用戶輸入的值,就得使用 onChange 事件:

// 示例2
var MyForm = React.createClass({
    getInitialState: function() {
        return {inputValue: 'defaultText'};
      },
    handleInputChange:function(e){
        this.setState({
            inputValue: e.target.value
        });
    },
    render:function(){
        return(
            <input type='text' value={this.state.inputValue} onChange={this.handleInputChange} />
            );
    }
});
ReactDOM.render(
   <MyForm />,
  document.getElementById('example')
);

示例 2 與示例 1 相比,最顯著的變化就是<input/> 的值存儲在父組件的 state 中,然後通過 onChange 事件去改變 state 。以下是示例2 的過程:

  • getlnitialState 設置defaultValue 。
  • < input/> 的值在渲染時被設置。
  • < input/ > 的值onChange 時, change 處理器被調用。
  • change 處理器更新state 。
  • 在重新撞染時更新< input/> 的值。

雖然與無約束組件相比,代碼量增加了不少,但是現在可以控制數據流,在用戶輸入數據時更新state 。

在示例2 的基礎上,我們改變handleInputChange,實現當用戶輸入時,把字符都轉換成大寫:

handleInputChange:function(e){
        this.setState({
            inputValue: e.target.value.toUpperCase()
        });
    },

你可能會注意到,在用戶輸入數據後,小寫字符轉成大寫形式並添加到輸入框時,並不會發生閃爍。這是因爲React 攔截了瀏覽器原生的change 事件,在setState 被調用後, 這個組件就會重新渲染輸入框。然後React 計算差異,更新輸入框的值。

不受限組件

沒有設置 value(或者設爲 null) 的 <input> 組件是一個不受限組件。對於不受限的 <input>組件,渲染出來的元素直接反應用戶輸入。不受限組件的最大的特點就是表單組件的值是不受React 組件控制的,而是由<input>自己控制。

// 示例3
var MyForm = React.createClass({
    render:function(){
        return(
            <input type='text' defaultValue='defaultText' />
            );
    }
});

ReactDOM.render(
   <MyForm />,
  document.getElementById('example')
);

我們可以通過defaultValue 屬性設置< input/ > 的默認值。組件的value 井非由父組件設置, 而是讓<input/>自己控制自己的值。

對於一個無約束的組件,如果要訪問DOM 節點的值,需要給<input/> 添加一個 ref 屬性。ref 是一個不屬於DOM 屬性的特殊屬性,用來標記DOM 節點,可以通過this 上下文訪問這個節點。爲了便於訪問,組件中所有的ref 都添加到了this.refs 上。

// 示例4
var MyForm = React.createClass({
    submitHandler:function(e){
        e.preventDefault();
        var value = ReactDOM.findDOMNode(this.refs.myInput).value;
        alert(value);
    },
    render:function(){
        return(
            <form onSubmit={this.submitHandler}>
                <input ref="myInput" type='text' defaultValue='defaultText' />
                <button type='submit'>提交</button>
            </form>
            );
    }
});

ReactDOM.render(
   <MyForm />,
  document.getElementById('example')
);

在示例4 中,我們設置了<input />的 ref ,然後通過this.refs.myInput 獲取到該節點,這樣就能獲取的節點的值了。

這裏需要注意一下版本兼容性問題:

當使用時this.refs.myInput.getDOMNode(),時如果使用 react 版本是 0.14.8及以前的版本,可以正常運行,但是如果使用的是0.15.0後的版本時,可能會報錯:

this.refs.myInput.getDOMNode is not a function(…)

這是需要用到 ReactDOM.findDOMNode(this.refs.myInput);。如果出現了相關的報錯,可以先打印一下this.refs.myInput,查看是否找到相應的節點,然後再查看不同版本對應的方法。

常用表單元素

除了上面<input />,下面介紹一些表單中常用的其他表單元素。

< label/ >

Label 是表單元素中很重要的組件,通過Label 可以明確地向用戶傳達你的要求,提升單選框和複選框的可用性。但是在使用Label 時需要注意 for 屬性,在React 中,與class 變成了className 類似, for 也變成了htmlFor :

<label htmlFor="name">Name:</label>

< textarea/ >

對 HTML 而言,讓開發者設置多行的值很容易。但是,React 是 JavaScript,沒有字符限制,可以使用 \n 實現換行。<textarea /> 被改得更像<input />了,允許我們設置value 和defaultValue 。

//非約束的
<textarea defaultValue="Hello World" />
// 約束的
<textarea value={this.state.textareaValue} onChange={this.handleChange} />

< select/ >

HTML 中<select>通常使用 <option> 的 selected 屬性設置選中狀態;React 爲了更方面的控制組件,<select />現在接受 value 和defaultValue 來設置已選項,我們可以更容易地對它的值進行操作。

//非約束的
<select defaultValue="B">
    <option value="A">First Option</option>
    <option value="B">Second Option</option>
    <option value="C">Third Option</option>
</select>
// 約束的
<select value={this.state . helloTo} onChange={this.handleChange}>
    <option value="A">First Option</option>
    <option value="B 與Second Option</option>
    <option value="C">Third Option</option>
</select>

< checkbox / >

複選框和單選框使用的則是另外一種完全不同的控制方式。在HTML 中,類型爲checkbox 或 radio 的 < input/ > 與類型爲text 的< input/> 的行爲完全不一樣。通常,複選框或者單選框的值是不變的,只有checked 狀態會變化。要控制複選框或者單選框,就要控制它們的checked 屬性。

我們先看一個簡單的示例:

// 示例5
var MyForm = React.createClass({
    getInitialState: function() {
        return {
            checked: true
        };
    },

    handleChange: function(event) {
        this.setState({
            checked: event.target.checked
        });
    },

    submitHandler: function(event) {
        event.preventDefault();
        alert(this.state.checked);
    },
    render: function() {
        return (
            <form onSubmit={this.submitHandler}>
                <input type="checkbox" value="A" checked={this.state.checked} onChange={this.handleChange} />
                <br/>
                <button type="submit">提交</button>
            </form>
        );
    }
});

上述<input/ > 的值一直都是A ,只有checked 的狀態在變化。我們把 checked 的狀態保存在 state 中,通過 state 的更新去渲染頁面效果。

一個簡單的表單示例

// 示例6
var MyForm = React.createClass({
    getInitialState:function(){
        return {
            inputValue: 'input value',
            selectValue: 'A',
            radioValue:'',
            checkValues:[],
            textareaValue:'some text here,,,,,'             
        }
    },
    handleSubmit:function(e){
        e.preventDefault();
        var formData = {
            input: ReactDOM.findDOMNode(this.refs.goodInput).value,
            select: ReactDOM.findDOMNode(this.refs.goodSelect).value,
            textarea: ReactDOM.findDOMNode(this.refs.goodTextarea).value,
            radio: this.state.radioValue,
            check: this.state.checkValues,
        }

        console.log('the form result is:')
        console.log(formData);

        this.refs.RadioButtons.saySomething();

    },
    handleRadio:function(e){
        this.setState({
            radioValue: e.target.value,
        })
    },
    handleCheck:function(e){
        var checkValues = this.state.checkValues.slice();
        var newVal = e.target.value;
        var index = checkValues.indexOf(newVal);
        if( index == -1 ){
            checkValues.push( newVal )
        }else{
            checkValues.splice(index,1);
        }

        this.setState({
            checkValues: checkValues,
        })
    },
    render: function(){
        return (
            <form onSubmit={this.handleSubmit}>
                <input ref="goodInput" type="text" defaultValue={this.state.inputValue }/>
                <br/>
                選項:
                <select defaultValue={ this.state.selectValue } ref="goodSelect">
                    <option value="A">A</option>
                    <option value="B">B</option>
                    <option value="C">C</option>
                    <option value="D">D</option>
                    <option value="E">E</option>
                </select>
                <br/>
                <p>radio button!</p>
                <RadioButtons ref="RadioButtons" handleRadio={this.handleRadio} />
                <br/>

                <Checkboxes handleCheck={this.handleCheck} />
                <br/>
                <textarea defaultValue={this.state.textareaValue} ref="goodTextarea"></textarea>
                <button type="submit">提交</button>

            </form>
        )
    }
});

var RadioButtons = React.createClass({
    saySomething:function(){
        alert("RadioButtons saying....");
    },
    render:function(){
        return (
            <span>
                A
                <input onChange={this.props.handleRadio} name="goodRadio" type="radio" value="A"/>
                B
                <input onChange={this.props.handleRadio} name="goodRadio" type="radio" value="B"/>
                C
                <input onChange={this.props.handleRadio} name="goodRadio" type="radio" value="C"/>
            </span>
        )
    }
});

var Checkboxes = React.createClass({
    render: function(){
        return (
            <span>
                A
                <input onChange={this.props.handleCheck}  name="goodCheckbox" type="checkbox" value="A"/>
                B
                <input onChange={this.props.handleCheck} name="goodCheckbox" type="checkbox" value="B" />
                C
                <input onChange={this.props.handleCheck} name="goodCheckbox" type="checkbox" value="C" />
            </span>
        )
    }
})

示例6 中將<radio/><checkbox/>封裝成了獨立的組件,提交表單時,利用this.refs 獲取對應的表單元素的值。這樣就不需要爲每一個組件寫一個獲取組件value 的函數了。渲染結果如下:

這裏寫圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章