十. 表单
10.1 受控组件
我们可以将表单写为受控组件:
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(this.state.value)
event.preventDefault()
}
render() {
return (
)
}
}
10.2 select 标签
在HTML中,select标签用来创建下拉列表标签。如下:
注意,由于selected属性的缘故,雪梨选项会被默认选中,但是在react中并不会使用selected属性,而是通过value把它变成受控组件。
class SelectComponent 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() {
alert(this.state.value)
}
render() {
return (
)
}
}
你可以将数组传递到value属性中,以支持select标签中选择多个选项
10.3 文件input 标签
因为它的value只读,所以它是React中的一个非受控组件
处理多个输入
当需要处理多个input的时候,我们可以给每个input设置一个 name
属性,然后根据 e.target.name
的值进行操作。
class MulInput extends React.Component {
constructor(props) {
super(props)
this.state = {
isGoing: false,
guestNumbers: 0
}
this.handleChange = this.handleChange.bind(this)
this.handleSubmit = this.handleSubmit.bind(this)
}
handleChange(event) {
const target = event.target
const name = target.name
const value = target.type === 'checkbox' ? target.checked : target.value
this.setState({
[name]: value
})
}
handleSubmit(event) {
console.log(this.state.isGoing)
console.log(this.state.guestNumbers)
event.preventDefault()
}
render() {
return (
)
}
}
十一. 状态提升
通常,多个组件需要反映相同的变化数据,这时我们建议将共享状态提升到共同的父组件去。
我们将创建一个给定水的温度判断水是否沸腾的温度计算器。
我们将从一个BoilingVerdict组件开始,它接受一个celsius温度作为prop,并根据该温度判断水是否沸腾返回不同的JSX
function BoilingVerdict(props) {
if (props.celsius >= 100) {
return 我开辽
} else {
return 我没开
}
}
接下来创建一个名为Calculator的组件,它渲染一个用于输入温度的 并将它保存到 this.state.temperature中。
另外根据它当前输入值渲染BoilingVerdict组件
class Calculator extends React.Component {
constructor(props) {
super(props)
this.state = {
temperature: 0
}
this.handleChange = this.handleChange.bind(this)
}
handleChange(event) {
this.setState({
temperature: event.target.value
})
}
render() {
const temperature = this.state.temperature
return (
)
}
}
添加第二个输入框
在已有摄氏度输入框的基础上,我们提供华氏度输入框,并保持两个输入框数据同步
首先我们先把input框提取出来
const scaleNames = {
c: "Celsius",
f: "Fahrenheit"
}
class TemperatureInput extends React.Component {
constructor(props) {
super(props)
this.state = {
temperature: ""
}
this.handleChange = this.handleChange.bind(this)
}
handleChange(event) {
this.setState({
temperature: event.target.value
})
}
render() {
const scale = this.props.myType
return (
)
}
}
class Calculator extends React.Component {
constructor(props) {
super(props)
this.state = {
temperature: 0
}
}
render() {
const temperature = this.state.temperature
return (
)
}
}
编写转换函数
function toCelsius(fahrenheit) {
return (fahrenheit - 32) * 5 / 9;
}
function toFahrenheit(celsius) {
return (celsius * 9 / 5) + 32;
}
上面两个函数仅仅做值的转换,下面我们接收一个函数,它接受temperature和一个函数作为参数并返回一个字符串,我们将用它来依据一个输入框的值计算另一个输入框的值
function tryConvert(temperature, convert) {
const input = parseFloat(temperature)
if (Number.isNaN(input)) {
return ''
}
const output = convert(input)
const rounded = Math.round(output * 1000) / 1000
return rounded.toString()
}
实现同步更新
首先,我们将TemperatureInput组件中的this.state.temperature替换为this.props.temperature
render() {
temperature = this.props.temperature
return (
...
)
}
现在,当temperatureInput想更新时,需要调用this.props.onTemperatureChange()来进行更新:
handleChange(event) {
this.props.onTemperatureChange(event.target.value)
}
const scaleNames = {
c: "Celsius",
f: "Fahrenheit"
}
// 华氏度转为摄氏度
function toCelsius(fahrenheit) {
return (fahrenheit - 32) * 5 / 9;
}
// 摄氏度转为华氏度
function toFahrenheit(celsius) {
return (celsius * 9 / 5) + 32;
}
// 传入一个温度和一个函数 根据是摄氏度还是华氏度对温度进行不同的转换
function tryConvert(temperature, convert) {
const input = parseFloat(temperature)
if (Number.isNaN(input)) {
return ''
}
const output = convert(input)
const rounded = Math.round(output * 1000) / 1000
return rounded.toString()
}
class TemperatureInput extends React.Component {
constructor(props) {
super(props)
this.handleChange = this.handleChange.bind(this)
}
handleChange(event) {
this.props.onTemperatureChange(event.target.value)
}
render() {
const scale = this.props.myType
return (
)
}
}
class Calculator extends React.Component {
constructor(props) {
super(props)
this.state = {
temperature: 0,
scale: 'c'
}
this.handleCelsiusChange = this.handleCelsiusChange.bind(this)
this.handleFahrenheitChange = this.handleFahrenheitChange.bind(this)
}
handleCelsiusChange(temperature) {
this.setState({temperature,scale: 'c'})
}
handleFahrenheitChange(temperature) {
this.setState({temperature,scale: 'f'})
}
render() {
const scale = this.state.scale
const temperature = this.state.temperature
const celsius = scale === 'f' ? tryConvert(temperature, toCelsius) : temperature
const fahrenheit = scale === 'c' ? tryConvert(temperature, toFahrenheit) : temperature
return (
)
}
}
首先,我们改变了
摄氏度input
的值,触发TemperatureInput组件的onChange事件TemperatureInput组件的onChange事件触发
this.props.onTemperatureChange(event.target.value)
并传入了一个摄氏度温度然后触发了
Calculator组件中的onTemperatureChange事件
事件对state进行了设置
this.setState({temperature,scale: 'c'})
Calculator组件中的state改变了
会触发 render() 方法render() { const scale = this.state.scale const temperature = this.state.temperature const celsius = scale === 'f' ? tryConvert(temperature, toCelsius) : temperature const fahrenheit = scale === 'c' ? tryConvert(temperature, toFahrenheit) : temperature return (
从而根据摄氏度的值得到了新的华氏度的值,华氏度input组件
的 this.props.temperature
的值进行了改变,华氏度input框的值也跟着改变