React 受控组件和非受控组件(表单组件获取值)

279 阅读5分钟

React表单事件

在HTML中,表单元素与其他元素最大的不同是它自带值或数据,有表单元素就会有用户输入,就会有表单事件触发,就会涉及的数据处理。在React开发应用时,为了更好地管理应用中的数据,响应用户的输入,就会运用到受控组件与非受控组件这两个概念。

受控组件与非受控组件

受控组件:受控组件的值由props或state传入,用户在元素上交互或输入内容会引起应用state的改变。在state改变之后重新渲染组件,我们才能在页面中看到元素中值的变化。假如组件没有绑定事件处理函数改变state,用户的输入是不会起到任何效果的,这也就是受控的含义所在。表单状态发生变化时,都会通知React,将状态交给React进行处理,React通过事件处理函数(e.target.value)获取到表单的值。

非受控组件:类似于传统的DOM表单控件,用户输入不会直接引起应用state的变化,我们也不会直接为非受控组件传入值。想要获取非受控组件,我们需要使用一个特殊的ref属性,同样也可以使用defaultValue属性来为其指定一次性的默认值。非受控组件的值不受组件自身的state和props控制,非受控组件使用ref从DOM中获取元素数据

受控组件简单来说就是它的值由React进行管理,而非受控组件的值则由原生DOM进行管理。

//受控组件
<input type='text' value={this.state.value} onChange={this.handleChange}/>

handleChange(event) {
	this.setState({value: event.target.value});
}


//非受控组件
<input type="text" defaultValue="hello!"/>
  • React中的组件分为受控组件和非受控组件

  • 受控组件的两个要点:

    • 组件的value属性与React中的状态绑定
    • 组件内声明了事件处理value的变化
  • 非受控组件更像是传统的HTML表单元素,数据存储在DOM中,而不是组件内部,获取数据的方式是通过ref引用

  • 使用总结:

    • 尽可能使用受控组件
    • 受控组件是将状态交由React处理,可以是任何元素,不局限于表单元素
    • 对于有大量表单元素的页面,使用受控组件会使程序变得繁琐难控,此时使用非受控组件更为明智
    • 在受控组件中,数据流是单向的(state是变化来源),因此在改变state时都应该使用setState,才会刷新页面
    • Refs不能用于函数式组件,因为函数式组件没有实例
    • 在函数式组件内部,是可以使用Refs的,需要引入hook

常见的表单组件获取值

在组件中声明表单元素input,textarea,select等时,一般都要为表单元素传入应用状态中的值,我们需要根据表单元素中用户的输入,对应用数据进行相应的操作和改变。常见的表单包括输入框,单选框,复选框,下拉框和多文本框,在Vue中只需要使用v-model指令,但是在React中需要自己实现。

输入框

先给input框的value绑定一个值,然后通过input或者change改变事件来获取用户输入的值,并将这个值赋值给value绑定的值,再使用setState方法来重新渲染组件就可以实现数据的双向绑定了。最后表单收集的就是用户输入的value值。

<input type="text" value={this.state.phonen} onChange={this.phonenhandle}/>
this.phonenhandle=(e)=>{
      console.log(e.target.value);
    };

ps:没有使用setState函数页面不会刷新,渲染用户输入的数据。

2.gif

<input type="text" value={this.state.phonen} onChange={this.phonenhandle}/>
this.phonenhandle=(e)=>{
      console.log(e.target.value);
      this.state.phonen=e.target.value;
      this.setState(this.state);
    };

2.gif

单选框和复选框

radio和checkbox通过操作checked属性实现受控,value只起辅助作用。

单选框可以通过onChange事件获取value的值,所以可以通过value值间接控制是否选中,但是onChange只能获取value值,不能改变,将选中的value值赋值给表单要收集的数据变量就可以收集到选中的value值。

<label>
    <input type="radio" name='sex' value='男' checked={this.state.sex=='男'} onChange={this.sexhandle}/>男
</label>
<label>
    <input type="radio" name='sex' value='女' checked={this.state.sex=='女'} onChange={this.sexhandle}/></label>
this.sexhandle = (e) => {
      console.log(e.target.value);
      this.state.sex = e.target.value;
      this.setState(this.state);
    };

2.gif

复选框checked属性是对数组进行操作,通过一个state的数组来存储变更。也是通过value值间接控制是否选中,onChange同样只能获取value值,不能改变,在数组中收集选中的value值。

<label>
    <input type="checkbox" value='篮球' checked={this.state.hobby.includes('篮球')} onChange={this.hobbyhandle} />篮球
</label>
<label>
    <input type="checkbox" value='高尔夫' checked={this.state.hobby.includes('高尔夫')} onChange={this.hobbyhandle} />高尔夫
</label>
<label>
    <input type="checkbox" value='乒乓球' checked={this.state.hobby.includes('乒乓球')} onChange={this.hobbyhandle} />乒乓球
</label>
<label>
    <input type="checkbox" value='躺平摆烂' checked={this.state.hobby.includes('躺平摆烂')} onChange=</label>
this.hobbyhandle = (e) => {
      console.log(e.target.value);
      if(this.state.hobby.includes(e.target.value)){
        // 已选中的再次点击取消选中,将value从数组中取消
        this.state.hobby.splice(this.state.hobby.indexOf(e.target.value),1);
      }else{
        // 未被选中的添加到数组中
        this.state.hobby.push(e.target.value)
      }
      console.log(this.state.hobby, 111);
      this.setState(this.state);
    }

2.gif

select下拉框

关于select,可以通过循环渲染option;在之前的select中,value都是写在option上的,但是在React中,对select进行了简单的封装,需要将value放在select中就可以通过select的onChange事件获取当前选择的value。

<select value={this.state.city} onChange={this.cityhandle}>
    {
        cityarr.map((value,index)=>{
        return <option key={index}>{value}</option>
    })
}
</select>
this.cityhandle = (e) => {
      this.state.city = e.target.value;
      console.log(this.state.city, 111);
      this.setState(this.state);
    };

2.gif

日期选择器

收集的是value值,就是选择的日期

<input type="date" value={this.state.date} onChange={this.datehandle} />
this.datehandle = (e) => {
      this.state.date = e.target.value;
      this.setState(this.state);
      console.log(this.state.date, 111);
    }

2.gif

文件类型

<input type="file" onChange={this.filehandle}/>
this.filehandle=(e)=>{
      console.log(e.target.files);
      this.state.file=e.target.files[0];
      this.setState(this.state);
    }

文本框

文本框和输入框的text同理,收集的是value就是用户输入的内容。

<textarea value={this.state.text} onChange={this.texthandle}></textarea>
this.texthandle=(e)=>{
      this.state.text=e.target.value;
      this.setState(this.state);
      console.log(this.state.text, 111);
    }

2.gif

import React, { Component } from 'react'

export default class Home extends Component {
  constructor() {
    super();
    this.state = {
      phonen: "",
      sex: '',
      city: '',
      hobby: [],
      date: '',
      text:'',
      file:''
    };
    this.phonenhandle = (e) => {
      console.log(e.target.value);
      this.state.phonen = e.target.value;
      this.setState(this.state);
    };
    this.sexhandle = (e) => {
      console.log(e.target.value);
      this.state.sex = e.target.value;
      this.setState(this.state);
    };
    this.cityhandle = (e) => {
      this.state.city = e.target.value;
      console.log(this.state.city, 111);
      this.setState(this.state);
    };
    this.hobbyhandle = (e) => {
      console.log(e.target.value);
      if (this.state.hobby.includes(e.target.value)) {
        // 已选中的再次点击取消选中,将value从数组中取消
        this.state.hobby.splice(this.state.hobby.indexOf(e.target.value), 1);
      } else {
        // 未被选中的添加到数组中
        this.state.hobby.push(e.target.value)
      }
      console.log(this.state.hobby, 111);
      this.setState(this.state);
    };
    this.datehandle = (e) => {
      this.state.date = e.target.value;
      this.setState(this.state);
      console.log(this.state.date, 111);
    };
    this.texthandle=(e)=>{
      this.state.text=e.target.value;
      this.setState(this.state);
      console.log(this.state.text, 111);
    };
    this.filehandle=(e)=>{
      console.log(e.target.files);
      this.state.file=e.target.files[0];
      this.setState(this.state);
    }
  }
  render() {
    let cityarr = ['请选择', '北京', '深圳', '杭州', '南京', '上海'];
    return (
      <div>
        <input type="text" value={this.state.phonen} onChange={this.phonenhandle}/>
        <br />
        <label>
          <input type="radio" name='sex' value='男' checked={this.state.sex=='男'} onChange={this.sexhandle}/></label>
        <label>
          <input type="radio" name='sex' value='女' checked={this.state.sex=='女'} onChange={this.sexhandle}/></label>
        <br />
        <label>
          <input type="checkbox" value='篮球' checked={this.state.hobby.includes('篮球')} onChange={this.hobbyhandle} />篮球
        </label>
        <label>
          <input type="checkbox" value='高尔夫' checked={this.state.hobby.includes('高尔夫')} onChange={this.hobbyhandle} />高尔夫
        </label>
        <label>
          <input type="checkbox" value='乒乓球' checked={this.state.hobby.includes('乒乓球')} onChange={this.hobbyhandle} />乒乓球
        </label>
        <label>
          <input type="checkbox" value='躺平摆烂' checked={this.state.hobby.includes('躺平摆烂')} onChange={this.hobbyhandle} />躺平摆烂
        </label>
        <br />
        <select value={this.state.city} onChange={this.cityhandle}>
          {
            cityarr.map((value,index)=>{
              return <option key={index}>{value}</option>
            })
          }
        </select>
        <br />
        <input type="date" value={this.state.date} onChange={this.datehandle} />
        <br />
        <textarea value={this.state.text} onChange={this.texthandle}></textarea>
        <br />
        <input type="file" onChange={this.filehandle}/>
      </div>
    )
  }
}