第一部分:表单基础
在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','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 = { '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中独特的自上而下的数据流。