React — 从表单认识的受控组件

2022-07-26,,,

表单

在React里,HTML表单元素的工作方式和其他的DOM的元素有些不用,这是因为表单元素通常会保持一些内部的state,例如这个纯html表单直接收一个名称:

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

此表单具有默认的HTML表单行为,即在用户提交表单后浏览到新页面。
如果你在React中执行相同的代码,他依然有效,但是大多数情况下,使用JavaScript函数可以很方便的处理表单的提交,同时还可以访问用户填写的表单数据。实现这种效果的标准方式是使用"受控组件"。

受控组件

在HTML中,表单元素(如,和)之类的表单元素通常自己维护state,并根据用户输入进行更新。而在React中,可变状态通常保存在组件的state中,并且只能通过setState来更新。

我们可以吧两者结合起来,使React的state成为唯一数据源。渲染表单的React组件还控制着用户输入过程中表单发生的操作。被React以这种方式控制取值的表单输入元素叫做“受控组件”。

假如,如果我们想让前一个实例在提交时候打印出名称,我么可以将表单写成受控组件:

class NameForm extends React.Component {
    constructor(props) {
        super(props)
        this.state = {
            value: props.value ? props.value : ""
        }


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

    handleSubmit = (event) => {
        alert('提交的名字: ' + this.state.value);
        event.preventDefault();
    }

    render(){

        return (
            <form onSubmit = { this.handleSubmit}>
                <div>{this.state.value}</div>
                <label>
                    名字:
                    <input type="text" value={this.state.value} onChange = { this.handleChange } />
                </label>
                <input type="submit" value="提交" />
            </form>
        )
    }
}

ReactDOM.render(<NameForm/>,document.querySelector('#root'))

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

对于受控组件来说,输入的值始终由React的state驱动。你也可以将value传递给其他UI元素,或者通过其他时间处理函数重置,但是意味着你需要编写更多的代码。

textarea 标签

在HTML中,<textarea>元素通过其子元素定义其文本:

<textarea>
  你好, 这是在 text area 里的文本
</textarea>

而在React中,使用value属性代替。这样,可以使得使用<textarea>的表单和使用单行的input的表单非常类似:

class TextareaControl extends React.Component {
    constructor(props) {
        super(props)
        this.state = {
            value: props.value ? props.value : ""
        }
    }
    handleSubmit = () => {
        alert(this.state.value)
    }
    handleChange = (ev) => {
        this.setState({
            value: ev.target.value
        })
    }
    render () {
        return (
            <form onSubmit={this.handleSubmit}>
                <div>{this.state.value}</div>
                <label>
                    名字:
                <textarea type="text" value={this.state.value} onChange={this.handleChange} />
                </label>
                <input type="submit" value="提交" />
            </form>
        )
    }
}

ReactDOM.render(<TextareaControl />, document.querySelector('#root'))

select标签

在HTML中,<select>创建下拉列表标签。例如,如下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 SelectControl extends TextareaControl {
    constructor(props) {
        super(props)
    }

    render () {
        return (
            <form onSubmit={this.handleSubmit}>
                <label>
                    选择你喜欢的风味:{this.state.value}
              <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>
        );
    }
}

ReactDOM.render(<SelectControl />, document.querySelector('#root'))

总的来说,这使得 , 和 之类的标签都非常相似—它们都接受一个 value 属性,你可以使用它来实现受控组件

文件input标签

在HTML中,<input type="file">允许用户从储存设备中选择一个或多个文件,将其上传到服务器,或者通过使用JavaScript的File API进行控制。

<input type="file">

因为它的value是只读,所以它是React中的一个非受控组件。

处理多个输入

当需要处理多个input元素时,我们可以给元素添加name属性,并让处理函数根据event.target.name的值选择要执行的操作。

class Reservation extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            isGoing: true,
            numberOfGuests: 2
        }
    }
    handleInputChange = (ev) => {
        const target = ev.target;
        const value = target.name === 'isGoing' ? target.checked : target.value;
        const name = target.name;
        this.setState({
            [name]: value
        })
    }
    render () {
        return (
            <form>
                {JSON.stringify(this.state)}
                <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>
        )
    }
}
ReactDOM.render( <Reservation />,document.querySelector("#root"))

这里使用了 ES6 计算属性名称的语法更新给定输入名称对应的 state 值:
等同于ES5的

var partialState = {};
partialState[name]=value;
this.setState(partialState)

另外由于setState自动将部分的state合并到当前state,只需要调用它更改部分的state即可。

受控输入空值

在受控组件上指定value的prop会阻止用户更改输入。如果你执行了value,单输入仍可编辑,则可能是你意外地将value设置成了undefined或null。

ReactDOM.render( <input value="hi"  onChange={()=>{}}/>,document.querySelector("#root"))
setTimeout(function() {
    ReactDOM.render(<input value={null} onChange={()=>{}} />, document.querySelector("#root"));
  }, 1000);

受控组件的代替品

有时使用受控组件会很麻烦,因为你需要为数据变化的每种方式编写时间处理函数,并通过一个React组件传递所有的输入state。当你将之前的代码库转化为React或将React应用程序与非React库集成的时,这可能令人厌烦。在这种情况下,你可能希望使用非受控组件。

本文地址:https://blog.csdn.net/qq_41914181/article/details/111146540

《React — 从表单认识的受控组件.doc》

下载本文的Word格式文档,以方便收藏与打印。