web前端高级React - React从入门到进阶之表单以及受控组件和非受控组件

系列文章目录

第一章:React从入门到进阶之初识React
第一章:React从入门到进阶之JSX简介
第三章:React从入门到进阶之元素渲染
第四章:React从入门到进阶之JSX虚拟DOM渲染为真实DOM的原理和步骤
第五章:React从入门到进阶之组件化开发及Props属性传值
第六章:React从入门到进阶之state及组件的生命周期
第七章:React从入门到进阶之React事件处理
第八章:React从入门到进阶之React条件渲染
第九章:React从入门到进阶之React中的列表与key
第十章:React从入门到进阶之表单及受控组件和非受控组件

React表单

在React中,HTML表单元素的工作方式和其它DOM元素有些不同,这是因为表单元素通常会保持一些内部的state。例如下面这段纯HTML表单只接受一个名称:

<form action="/" >
	<label>
		名字:
		<input type="text" name="name" />
	</label>
	<input type="submit" value="提交" />
</form>

此表单具有默认的HTML表单行为,即用户会将表单内容提交到form的action所指定的新页面。如果我们在React中执行相同的代码,它依然有效。但大多数情况下,使用JavaScript函数也可以很方便的处理表单提交,同时也可以访问用户填写的表单数据。而实现这种效果的标准方式是使用“受控组件”。

受控组件

  • 在HTML中,表单元素(如input、textarea、select)等之类的表单元素通常自己维护state并根据用户输入进行更新。 而在React中,可变状态通过保存在组件的state属性中,并且只能通过使用setState来更新。
  • 我们可以把两者结合起来,是React的state成为表单元素的“唯一数据源”。并且渲染表单的React组件还控制着用户输入过程中表单发生的变化。我们把被React以这种方式控制取值的表单输入元素称为“受控组件”
  • 其实说白了,可以理解为以下四点:
    • 1.React组件中的state属性是表单元素的“唯一数据源”,如value={this.state.name}
    • 2.每当表单元素的值发生变化时必须调用元素的onChange事件处理器
    • 3.在事件处理器中通过合成事件对象e拿到改变后的状态值,并更新到state中
    • 4.在onChange事件处理器中通过调用setState方法来触发视图重新渲染,完成表单组件值的更新。否则你会发现不管输入什么页面中的值始终都是初始值不会变化。

下面看一个小例子来深入理解一下

class Myform extends React.Component {
    constructor() {
        super()
        this.state = {
            name: "Alvin"
        }
    }
    render() {
        return <div>
            <input type="text" value={this.state.name} onChange={this.change.bind(this)}></input>
        </div>
    }

    change(e) {
        console.log(e.target.value);
        this.setState({
            name: e.target.value
        })
    }
}
  • 上面代码示例中,我们定义了一个Myform组件,在组件中定义一个state对象保存一个name属性,用于作为表单元素的初始值
  • 在render方法中我们把state的name属性值作为input的value值,并给input绑定一个onChange事件
  • 然后在onChange事件中,利用合成对象e获取input的输入值,并调用setState方法触发组件的重新渲染
  • 最后把更新后的值展示在页面上

由于在表单元素上设置了 value 属性,因此显示的值将始终为 this.state.value,这使得 React 的 state 成为唯一数据源。由于 change 在每次按键时都会执行并更新 React 的 state,因此显示的值将随着用户输入而更新。

非受控组件

  • 上面已经介绍了什么是受控组件,下面我们来继续看一下什么是非受控组件
  • 在大多数情况下,我们推荐使用受控组件来处理表单数据。在一个受控组件中,表单数据是由React组件来管理的。另一种替代方案就是我们即将要介绍的“非受控组件”,这时表单数据将交由DOM节点来处理。
  • 其实简单来说就是:不需要通过state属性来控制元素的值,而是通过组件的ref来直接操作真是DOM,从而达到更新值的目的,这就是所谓的“非受控组件”
class Myform extends React.Component {
    render() {
        return <div>
            <input type="text" value="Alvin" ref={name=>this.input=name} onChange={this.change.bind(this)}></input>
        </div>
    }

    change(e) {
       this.input.current.value
    }
}
  • 在上面的代码示例中,我们并没有通过state来设置元素的值,而是直接给赋值了一个字符串,并且给元素添加了个ref属性,而在onChange事件中,我们直接通过ref来操作DOM获取元素的值。
  • 因为非受控组件将真实数据储存在 DOM 节点中,所以在使用非受控组件时,有时候反而更容易同时集成 React 和非 React 代码。如果不介意代码美观性,并且希望快速编写代码,使用非受控组件往往可以减少我们的代码量。否则,我们应该使用受控组件,而且也推荐使用受控组件。

textarea 标签

  • 在 HTML 中, 元素通过其子元素定义其文本:
  • 而在 React 中, 使用 value 属性代替。这样,可以使得使用 的表单和使用单行 input 的表单非常类似:
class EssayForm extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      value: '请撰写一篇关于你喜欢的 DOM 元素的文章.'
    };

    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 (
      <form onSubmit={this.handleSubmit}>
        <label>
          文章:
          <textarea value={this.state.value} onChange={this.handleChange} />
        </label>
        <input type="submit" value="提交" />
      </form>
    );
  }
}

请注意,this.state.value 初始化于构造函数中,因此文本区域默认有初值。

select 标签

在 HTML 中, 创建下拉列表标签。例如,如下 HTML 创建了水果相关的下拉列表:

<select>
  <option value="grapefruit">葡萄柚</option>
  <option value="lime">酸橙</option>
  <option selected value="coconut">椰子</option>
  <option value="mango">芒果</option>
</select>

但需要注意的是:由于 selected 属性的缘故,椰子选项默认被选中。然而React 并不会使用 selected 属性,而是在根 select 标签上使用 value 属性。这在受控组件中更便捷,因为我们只需要在根标签中更新它。例如:

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('你喜欢的风味是: ' + this.state.value);
    event.preventDefault();
  }

  render() {
    return (
      <form onSubmit={this.handleSubmit}>
        <label>
          选择你喜欢的风味:
          <select value={this.state.value} onChange={this.handleChange}>
            <option value="grapefruit">葡萄柚</option>
            <option value="lime">酸橙</option>
            <option value="coconut">椰子</option>
            <option value="mango">芒果</option>
          </select>
        </label>
        <input type="submit" value="提交" />
      </form>
    );
  }
}
  • 总的来说,这使得 , 和 之类的标签都非常相似—它们都接受一个 value 属性,我们可以使用它来实现受控组件。
  • 另外:我们可以将数组传递到 value 属性中,以支持在 select 标签中选择多个选项:
<select multiple={true} value={['B', 'C']}>

文件input标签

  • 在 HTML 中,< input type=“file”> 允许用户从存储设备中选择一个或多个文件,将其上传到服务器,或通过使用 JavaScript 的 File API 进行控制
  • 但是在React中它始终是一个非受控组件,因为它的值只能由用户设置,而不能通过代码控制。
  • 下面的例子显示了如何创建一个 DOM 节点的 ref 从而在提交表单时获取文件的信息。
class FileInput extends React.Component {
  constructor(props) {
    super(props);
    this.handleSubmit = this.handleSubmit.bind(this);
    this.fileInput = React.createRef();
  }
  handleSubmit(event) {
    event.preventDefault();
    alert(
      `Selected file - ${this.fileInput.current.files[0].name}`
    );
  }

  render() {
    return (
      <form onSubmit={this.handleSubmit}>
        <label>
          Upload file:
          <input type="file" ref={this.fileInput} />
        </label>
        <br />
        <button type="submit">Submit</button>
      </form>
    );
  }
}

ReactDOM.render(
  <FileInput />,
  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.name === 'isGoing' ? target.checked : target.value;
    const name = target.name;

    this.setState({
      [name]: value
    });
  }

  render() {
    return (
      <form>
        <label>
          参与:
          <input
            name="isGoing"
            type="checkbox"
            checked={this.state.isGoing}
            onChange={this.handleInputChange} />
        </label>
        <br />
        <label>
          来宾人数:
          <input
            name="numberOfGuests"
            type="number"
            value={this.state.numberOfGuests}
            onChange={this.handleInputChange} />
        </label>
      </form>
    );
  }
}
  • 还需要注意一点:这个例子中使用了 ES6 计算属性名称的语法更新给定输入名称对应的 state 值:
    例如:
this.setState({
 [name]: value
});

等同于ES5:

var partialState = {};
partialState[name] = value;
this.setState(partialState);
  • 另外,由于 setState() 自动将部分 state 合并到当前 state, 只需调用它更改部分 state 即可。

受控输入空值

  • 在受控组件上,如果我们指定了value的prop则会阻止用户更改输入。就是说如果我们给元素添加了value属性,如果不通过onChange和setState重新渲染,那么我们是无法更新元素内容的,所以说会阻止用户更改输入
  • 但是我们给元素指定了value,用户输入却仍然可以编辑,这说明我们可能意外的把value的值设置成了undefined或null了。
  • 下面的代码演示了这一点。(输入最初被锁定,但在短时间延迟后变为可编辑。)
ReactDOM.render(<input value="hi" />, mountNode);

setTimeout(function() {
  ReactDOM.render(<input value={null} />, mountNode);
}, 1000);

好了,以上就是本节的全部内容了
下一章节中我们将会继续给大家讲解React中的状态提升

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值