HTML表单元素与React中的其他DOM元素有所不同,因为表单元素生来就保留一些内部状态。
当用户提交表单时,HTML的默认行为会使这个表单跳转到一个新页面。在React中亦是如此。但大多数情况下,我们都会构造一个处理提交表单并可访问用户输入表单数据的函数。实现这一点的标准方法是使用一种称为“受控组件”的技术。
受控组件
在HTML当中,像<input>
,<textarea>
, 和 <select>
这类表单元素会维持自身状态,并根据用户输入进行更新。但在React中,可变的状态通常保存在组件的状态属性中,并且只能用setState()
方法进行更新。
我们通过使react变成一种单一数据源的状态来结合二者。React负责渲染表单的组件仍然控制用户后续输入时所发生的变化。相应的,其值由React控制的输入表单元素称为“受控组件”。
class NameForm extends React.Component {
constructor(props) { //构造函数
super(props);
this.state = {value: ''};
this.handleChange = this.handleChange.bind(this); //绑定this将直接调用this.handle.Change变为方法调用
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(event) { //注意这里的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}> {/*onSubmit必须写在form标签里吗*/}
<label> {/*lable标签辅助选中*/}
Name:
<input type="text" value={this.state.value} onChange={this.handleChange} />
</label>
<input type="submit" value="Submit" />
</form>
);
}
}
ReactDOM.render(
<NameForm />,
document.getElementById('root')
);
由于 value
属性是在我们的表单元素上设置的,因此显示的值将始终为 React数据源上this.state.value
的值。由于每次按键都会触发 handleChange
来更新当前React的state,所展示的值也会随着不同用户的输入而更新。
使用”受控组件”,每个状态的改变都有一个与之相关的处理函数。这样就可以直接修改或验证用户输入。例如,我们如果想限制输入全部是大写字母,我们可以将handleChange
写为如下:
handleChange(event) {
this.setState({value: event.target.value.toUpperCase()});
}
class FlavorForm extends React.Component {
constructor(props) {
super(props);
this.state = {value: 'coconut'}; //初始状态中的value值来代表默认选中项
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')
);
多个输入的解决方法
当你有处理多个受控的input
元素时,你可以通过给每个元素添加一个name
属性,来让处理函数根据 event.target.name
的值来选择做什么。
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; //根据name的值决定将value赋给谁
this.setState({
[name]: value
});
}
render() {
return (
<form>
<label>
Is going:
<input
name="isGoing"
type="checkbox"
checked={this.state.isGoing} //同过this.state.isGoing决定是否选中了,赋给属性checked,最后传递给value
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')
);
接下来,我们写一个名为 Calculator
的组件。它会渲染一个 <input>
来接受用户输入,然后将输入的温度值保存在 this.state.temperature
当中。
之后呢,它会根据输入的值渲染出 BoilingVerdict
组件。
function BoilingVerdict(props) {
if (props.celsius >= 100) {
return <p>The water would boil.</p>;
}
return <p>The water would not boil.</p>;
}
class Calculator extends React.Component {
constructor(props) {
super(props);
this.handleChange = this.handleChange.bind(this);
this.state = {temperature: ''};
}
handleChange(e) {
this.setState({temperature: e.target.value}); //输入值赋给temperature变量
}
render() {
const temperature = this.state.temperature;
return (
<fieldset>
<legend>Enter temperature in Celsius:</legend>
<input
value={temperature} //决定显示在文本框里
onChange={this.handleChange} />
<BoilingVerdict
celsius={parseFloat(temperature)} /> {/*parseFloat() 函数可解析一个字符串,并返回一个浮点数。
该函数指定字符串中的首个字符是否是数字。如果是,则对字符串进行解析,直到到达数字的末端为止,然后以数字返回该数字,而不是作为字符串。*/}
</fieldset>
);
}
}
ReactDOM.render(
<Calculator />,
document.getElementById('root')
);
function FancyBorder(props) {
return (
<div className={'FancyBorder FancyBorder-' + props.color}>
{props.children}
</div>
);
}
function Dialog(props) {
return (
<FancyBorder color="blue">
<h1 className="Dialog-title">
{props.title}
</h1>
<p className="Dialog-message">
{props.message}
</p>
</FancyBorder>
);
}
function WelcomeDialog() {
return (
<Dialog
title="Welcome" //=号右边的值作为props.message
message="Thank you for visiting our spacecraft!" />
);
}
ReactDOM.render(
<WelcomeDialog />,
document.getElementById('root')
);
function FancyBorder(props) {
return (
<div className={'FancyBorder FancyBorder-' + props.color}>
{props.children}
</div>
);
}
function Dialog(props) {
return (
<FancyBorder color="blue">
<h1 className="Dialog-title">
{props.title}
</h1>
<p className="Dialog-message">
{props.message}
</p>
{props.children}
</FancyBorder>
);
}
class SignUpDialog extends React.Component {
constructor(props) {
super(props);
this.handleChange = this.handleChange.bind(this);
this.handleSignUp = this.handleSignUp.bind(this);
this.state = {login: ''};
}
render() {
return (
<Dialog title="Mars Exploration Program"
message="How should we refer to you?">
<input value={this.state.login}
onChange={this.handleChange} />
<button onClick={this.handleSignUp}>
Sign Me Up!
</button>
</Dialog>
);
}
handleChange(e) {
this.setState({login: e.target.value}); //这里login是一个属性名
}
handleSignUp() {
alert(`Welcome aboard, ${this.state.login}!`);
}
}
ReactDOM.render(
<SignUpDialog />,
document.getElementById('root')
);