react的表单

学习书籍:深入React技术栈

1、前言

在 Web 应用开发中,表单的作用尤为重要。正是因为表单的存在,才使得用户能够与 Web 应用进行富交互。而在React 中,一切数据都是状态,当然也包括表单数据。在这一节中,我们将讲述 React 是如何处理表单的。

2、应用表单组件(受控组件)

表单组件的几个重要属性:

  a、状态属性:React 的 form 组件提供了几个重要的属性,用于展示组件的状态。

  • value:类型为 text 的 input 组件、textarea 组件以及 select 组件都借助 value prop 来展示应用的状态。
  • checked:类型为 radio 或 checkbox 的组件借助值为 boolean 类型的 selected prop 来展示应用的状态。
  •  selected:该属性可作用于 select 组件下面的 option 上,React 并不建议使用这种方式表示状态,而推荐在 select 组件上使用 value 的方式。

b、事件属性

刚刚提到的状态属性与事件属性存在一定的关联——在状态属性发生变化时,会触发onChange 事件属性。实际上,受控组件中的 change 事件与 HTML DOM 中提供的 input 事件更为类似。同样,React 支持 DOM Level 3 中定义的所有表单事件。

2、1 文本框

JSX 中的 textarea 组件与类型为 text 的 input 组件的用法很类似。同样有一个 value prop 用来表示表单的值,而在 HTML 中 textarea 的值则是通过children 来表示的。此外,得益于 JSX 语法特性,我们可以在标签没有子元素的时候使用单个标签自闭合的语法。

import React, {Component} from 'react';
import './App.css';

class App extends Component {
    constructor(props) {
        super(props);
        this.handleInputChange = this.handleInputChange.bind(this);
        this.handleTextareaChange = this.handleTextareaChange.bind(this);
        this.state = {
            inputValue: '',
            textareaValue: '',
        };
    }
    handleInputChange(e) {
        this.setState({
            inputValue: e.target.value,
        });
    }
    handleTextareaChange(e) {
        this.setState({
            textareaValue: e.target.value,
        });
    }
    render() {
        const {inputValue, textareaValue} = this.state;
        return (
            <div>
                <p>单行输入框:<input type="text" value={inputValue}
                                onChange={this.handleInputChange}/></p>
                <p>多行输入框:<textarea value={textareaValue}
                                   onChange={this.handleTextareaChange}/></p>
            </div>
        );
    }
}

export default App;

2、2 单选按钮与复选框、select

单选和复选框这两种表单的 value 值一般是不会改变的,而是通过一个布尔类型的checked prop 来表示是否为选中状态。对于select,其中通过设置 select标签的 multiple={true} 来实现一个多选下拉列表。

import React, {Component} from 'react';
import './App.css';

class App extends Component {
    constructor(props) {
        super(props);
        this.state = {
            radios: '',
            select: '',
            multipleSelect: [],
            checkbox: [],
        };
    }
    handleChange(name,event) {
        var  _this = this,
            setState = {
                checkbox: this.state.checkbox
            };
        // console.log(name,event.target,event.target.value);

        if (name == 'checkbox') {
            if (event.target.checked) {
                if (setState[name].indexOf(event.target.value) == -1) {
                    setState[name].push(event.target.value);
                }
            } else {
                var findIndex = setState.checkbox.findIndex( item => item == event.target.value);

                setState.checkbox.splice(findIndex,1);
            }
        } else if (name == 'multipleSelect') {

            var { options } = event.target,
                multipleSelect = Object.keys(options)
                    .filter(i => options[i].selected === true)
                    .map(i => options[i].value);
            setState[name] = multipleSelect;
        } else{
            setState[name] = event.target.value;
        }

        _this.setState(setState);
    }
    render() {
        const { radios,checkbox,select,multipleSelect} = this.state;
        return (
            <div>
                <p>radio:</p>
                <label>
                    male:
                    <input
                        type="radio"
                        name="radios"
                        value="male"
                        checked={radios === 'male'}
                        onChange={this.handleChange.bind(this,'radios')}
                    />
                </label>
                <label>
                    female:
                    <input
                        type="radio"
                        value="female"
                        name="radios"
                        checked={radios === 'female'}
                        onChange={this.handleChange.bind(this,'radios')}
                    />
                </label>

                <br/>

                <p>checkbox:</p>
                <label>
                    <input
                        type="checkbox"
                        value="aa"
                        checked={checkbox.indexOf('aa') !== -1}
                        onChange={this.handleChange.bind(this,'checkbox')}
                    />
                    aa
                </label>
                <label>
                    <input
                        type="checkbox"
                        value="asdd"
                        checked={checkbox.indexOf('asdd') !== -1}
                        onChange={this.handleChange.bind(this,'checkbox')}
                    />
                    asdd
                </label>
                <label>
                    <input
                        type="checkbox"
                        value="cc"
                        checked={checkbox.indexOf('cc') !== -1}
                        onChange={this.handleChange.bind(this,'checkbox')}
                    />
                   cc
                </label>

                <br/>

                <p>multipleSelect</p>
                <label>
                    <select value={multipleSelect}   multiple={true} onChange={this.handleChange.bind(this,'multipleSelect')}>
                        <option value="beijing" >北京</option>
                        <option value="shanghai">上海</option>
                        <option value="hangzhou">杭州</option>
                    </select>
                </label>

                <p>select</p>
                <label>
                    <select value={select}  onChange={this.handleChange.bind(this,'select')}>
                        <option value="beijing">北京</option>
                        <option value="shanghai">上海</option>
                        <option value="hangzhou">杭州</option>
                    </select>
                </label>
            </div>
        )
    }
}

export default App;

每当表单的状态发生变化时,都会被写入到组件的 state 中,这种组件在React 中被称为受控组件(controlled component)。在受控组件中,组件渲染出的状态与它的 value或 checked prop 相对应。React 通过这种方式消除了组件的局部状态,使得应用的整个状态更加。可控。React 官方同样推荐使用受控表单组件。总结下 React 受控组件更新 state 的流程:

  • 可以通过在初始 state 中设置表单的默认值。
  • 每当表单的值发生变化时,调用 onChange 事件处理器。
  • 事件处理器通过合成事件对象event 拿到改变后的状态,并更新应用的 state。
  • setState 触发视图的重新渲染,完成表单组件值的更新。

在 React 中,数据是单向流动的。从示例中,我们能看出来表单的数据源于组件的 state,并通过 props 传入,这也称为单向数据绑定。然后,我们又通过 onChange 事件处理器将新的表单数据写回到组件的 state,完成了双向数据绑定。

3、非受控组件

一个表单组件没有 value props(单选按钮和复选框对应的是 checked prop)时,就可以称为非受控组件。相应地,你可以使用 defaultValue 和 defaultChecked prop 来表示组件的默认状态。下面通过一个简单的示例来描述非受控组件

import React, {Component} from 'react';
import './App.css';

class App extends Component {
    constructor(props) {
        super(props);
        this.handleSubmit = this.handleSubmit.bind(this);
    }
    handleSubmit(e) {
        e.preventDefault();
        // 这里使用 React 提供的 ref prop 来操作 DOM
        // 当然,也可以使用原生的接口,如 document.querySelector
        const { value } = this.refs.name;
        console.log(value,document.querySelector('.name').value);
    }

    render() {
        return (
            <form onSubmit={this.handleSubmit}>
                <input ref="name" className="name" type="text" defaultValue="Hangzhou" />
                <button type="submit">Submit</button>
            </form>
        )
    }
}

export default App;

在 React 中,非受控组件是一种反模式,它的值不受组件自身的 state 或 props 控制。通常,需要通过为其添加 ref prop 来访问渲染后的底层 DOM 元素。

4、非受控组件和受控组件总结

  • 过 defaultValue 或者 defaultChecked 来设置表单的默认值,它仅会被渲染一次,在后续的渲染时并不起作用。在受控组件中,可以将用户输入的英文字母转化为大写后输出展示,而在非受控组件中则不会。而如果不对受控组件绑定 change 事件,我们在文本框中输入任何值都不会起作用。多数情况下,对于非受控组件,我们并不需要提供 change 事件。通过上面的示例可以看出,受控组件和非受控组件的最大区别是:非受控组件的状态并不会受应用状态的控制,应用中也多了局部组件状态,而受控组件的值来自于组件的 state
    import React, {Component} from 'react';
    import './App.css';
    
    class App extends Component {
        constructor(props) {
            super(props);
            this.state = {
                value1: '',
                value2: '',
            };
    
        }
    
        render() {
            return (
                <div>
                    <input
                        value={this.state.value1}
                        onChange={e => {
                            this.setState({ value1: e.target.value.toUpperCase() })
                        }}
                    />
                    <input
                        defaultValue={this.state.value2}
                        onChange={e => {
                            this.setState({ value2: e.target.value.toUpperCase() })
                        }}
                    />
                </div>
            )
        }
    }
    
    export default App;

     

 

 

  • 性能上的问题:在受控组件中,每次表单的值发生变化时,都会调用一次 onChange 事件处理器,这确实会有一些性能上的损耗。虽然使用非受控组件不会出现这些问题,但仍然不提倡在 React 中使用非受控组件。这个问题可以通过 Flux/Redux 应用架构等方式来达到统一组件状态的目的。
  • 是否需要事件绑定:使用受控组件最令人头疼的就是,我们需要为每个组件绑定一个 change 事件,并且定义一个事件处理器来同步表单值和组件的状态,这是一个必要条件。当然,在某些简单的情况下,也可以使用一个事件处理器来处理多个表单域。

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值