第一部分:表單基礎
在React中,修改表單的唯一途徑是使用setState方法。舉例如下:
class NameForm extends React.Component { constructor(props) { super(props); this.state = {value: ''}; this.handleChange = this.handleChange.bind(this); this.handleSubmit = this.handleSubmit.bind(this); } handleChange(event) { this.setState({value: event.target.value}); } handleSubmit(event) { alert('A name was submitted: ' + this.state.value); event.preventDefault(); } render() { return ( <form onSubmit={this.handleSubmit}> <label> Name: <input type="text" value={this.state.value} onChange={this.handleChange} /> </label> <input type="submit" value="Submit" /> </form> ); } } ReactDOM.render( <NameForm />, document.getElementById('root') );
在codepen的運行代碼連接
我們可以看出其運行邏輯:首先將組建渲染到頁面,及執行了render(),此時獲取的value為空,當我們輸入數據時,觸發handleChange函數(注意:要提前綁定在當前環境下),然后設置state中的value為用戶當前輸入值,於是表單元素input獲取到最新的state並使用虛擬dom與真實dom作對比,只更新有變化的dom... 當點擊提交按鈕時,觸發了handleSubmit函數。
值得注意的是:在handleSubmit函數中,我們使用event.preventDefault()阻止了默認行為,即:提交表單后,不會自動reset表單,而是保留之前的用戶數據!
第二部分:textarea表單
textarea表單和Input表單本身是沒有什么區別的,舉例如下所示:
class EssayForm extends React.Component { constructor(props) { super(props); this.state = { value: 'Please write an essay about your favorite DOM element.' }; this.handleChange = this.handleChange.bind(this); this.handleSubmit = this.handleSubmit.bind(this); } handleChange(event) { this.setState({value: event.target.value}); } handleSubmit(event) { alert('An essay was submitted: ' + this.state.value); event.preventDefault(); } render() { return ( <form onSubmit={this.handleSubmit}> <label> Name: <textarea value={this.state.value} onChange={this.handleChange} style={{color:'red',width:'400px',height:'15px'}} /> </label> <input type="submit" value="Submit" /> </form> ); } } ReactDOM.render( <EssayForm/>, document.getElementById('root') );
這里我設置了初始狀態,所以一開始我們就可以在textarea中看到內容,稍有不同的是,我還在textarea中設置了樣式(注意:要用兩個curly brace,外面的表示包含js對象,里面的表示包含一個樣式對象,當然我們也可以在外面先定義對象然后再傳進來)。
另外,我們還可以直接在css中設置樣式,如下所示:
textarea{ background:red; color:white !important; }
這樣,背景顏色為紅色,字體為白色。
注意:因為在React中設置的style是行內樣式,優先級較高,故在外聯樣式中無法覆蓋,只有通過使用!important的方式才能成功覆蓋。
另外,將樣式對象傳入的方法如下:
render() { var myStyle = { width:'400px', height:'15px', color:'red' }; return ( <form onSubmit={this.handleSubmit}> <label> Name: <textarea value={this.state.value} onChange={this.handleChange} style={myStyle} /> </label> <input type="submit" value="Submit" /> </form> );
第三部分:select表單
例子如下所示:
class FlavorForm extends React.Component { constructor(props) { super(props); this.state = {value: 'coconut'}; this.handleChange = this.handleChange.bind(this); this.handleSubmit = this.handleSubmit.bind(this); } handleChange(event) { this.setState({value: event.target.value}); } handleSubmit(event) { alert('Your favorite flavor is: ' + this.state.value); event.preventDefault(); } render() { return ( <form onSubmit={this.handleSubmit}> <label> Pick your favorite La Croix flavor: <select value={this.state.value} onChange={this.handleChange}> <option value="grapefruit">Grapefruit</option> <option value="lime">Lime</option> <option value="coconut">Coconut</option> <option value="mango">Mango</option> </select> </label> <input type="submit" value="Submit" /> </form> ); } } ReactDOM.render( <FlavorForm />, document.getElementById('root')
);
第四部分:復雜表單
舉例如下所示:
class Reservation extends React.Component { constructor(props) { super(props); this.state = { isGoing: true, numberOfGuests: 2 }; this.handleInputChange = this.handleInputChange.bind(this); } handleInputChange(event) { const target = event.target; const value = target.type === 'checkbox' ? target.checked : target.value; const name = target.name; this.setState({ [name]: value }); } render() { return ( <form> <label> Is going: <input name="isGoing" type="checkbox" checked={this.state.isGoing} onChange={this.handleInputChange} /> </label> <br /> <label> Number of guests: <input name="numberOfGuests" type="number" value={this.state.numberOfGuests} onChange={this.handleInputChange} /> </label> </form> ); } } ReactDOM.render( <Reservation />, document.getElementById('root') );
第五部分:lifting state up
舉例如下:
const scaleNames = { c: 'Celsius', f: 'Fahrenheit' }; class TemperatureInput extends React.Component { constructor(props) { super(props); this.handleChange = this.handleChange.bind(this); this.state = {value: '545445'}; } handleChange(e) { this.setState({value: e.target.value}); } render() { const value = this.state.value; const scale = this.props.scale; return ( <fieldset> <legend>Enter temperature in {scaleNames[scale]}:</legend> <input value={value} onChange={this.handleChange} /> </fieldset> ); } } class Calculator extends React.Component { render() { return ( <div> <TemperatureInput scale="c" /> <TemperatureInput scale="f" /> </div> ); } } ReactDOM.render( <Calculator />, document.getElementById('root') );
兩個input聯動
const scaleNames = { c: 'Celsius', f: 'Fahrenheit' }; function toCelsius(fahrenheit) { return (fahrenheit - 32) * 5 / 9; } function toFahrenheit(celsius) { return (celsius * 9 / 5) + 32; } function tryConvert(value, convert) { const input = parseFloat(value); if (Number.isNaN(input)) { return ''; } const output = convert(input); const rounded = Math.round(output * 1000) / 1000; return rounded.toString(); } function BoilingVerdict(props) { if (props.celsius >= 100) { return <p>The water would boil.</p>; } return <p>The water would not boil.</p>; } class TemperatureInput extends React.Component { constructor(props) { super(props); this.handleChange = this.handleChange.bind(this); } handleChange(e) { this.props.onChange(e.target.value); } render() { const value = this.props.value; const scale = this.props.scale; return ( <fieldset> <legend>Enter temperature in {scaleNames[scale]}:</legend> <input value={value} onChange={this.handleChange} /> </fieldset> ); } } class Calculator extends React.Component { constructor(props) { super(props); this.handleCelsiusChange = this.handleCelsiusChange.bind(this); this.handleFahrenheitChange = this.handleFahrenheitChange.bind(this); this.state = {value: '', scale: 'c'}; } handleCelsiusChange(value) { this.setState({scale: 'c',value}); } handleFahrenheitChange(value) { this.setState({scale: 'f',value}); } render() { const scale = this.state.scale; const value = this.state.value; const celsius = scale === 'f' ? tryConvert(value, toCelsius) : value; const fahrenheit = scale === 'c' ? tryConvert(value, toFahrenheit) : value; return ( <div> <TemperatureInput scale="c" value={celsius} onChange={this.handleCelsiusChange} /> <TemperatureInput scale="f" value={fahrenheit} onChange={this.handleFahrenheitChange} /> <BoilingVerdict celsius={parseFloat(celsius)} /> </div> ); } } ReactDOM.render( <Calculator />, document.getElementById('root') );
值得注意的是: 如果幾個組件都需要訪問(獲取)相同的state, 那么這是一個我們需要將state提升(listing)到他們幾個共同的相同的祖先組件中的信號!在我們的例子中,就是Calculator。 我們存儲了value 和 scale 在Calculator的state中。
兩個input將會保持同步,因為他們基於相同的state。
不管你在哪個input中輸入,this.state.value和this.state.scale都會更新。
即通常,如果一個組件需要state來render,這時我們可以將之添加到這個組件中,但是如果另外一個組件也需要這個state,我們就可以把這個state提升到兩者最近的祖先中去。相反於嘗試着去同步兩個不同組件的state,我們應當依賴於top-down data flow,即React中獨特的自上而下的數據流。