浅析React中的受控组件和非受控组件

本文翻译自Controlled and uncontrolled form inputs in React don’t have to be complicated。如有错误,请不吝指正。

React中的受控和非受控表单并不复杂

这里写图片描述
你可能看到过很多文章,说不应该使用setStaterefs不好……这是自相矛盾的。很难理解怎样是对的,甚至连选择的规则都不清楚。

那我们应该怎么解决表单这个棘手的问题呢?

毕竟表单还是某些web应用的核心。可是处理表单好像变成了绊脚石?

不用害怕了。我会告诉你这两种方式的区别和使用场景。

The Uncontrolled

非控表单输入类似于传统的HTML表单输入:

class Form extends Component{
    render(){
        return (
            <div>
                <input type="text" />
            </div>
        );
    }
}

他们记得你输入了什么。你可以使用ref获取他们的值。例如,在按钮的onClick处理方法中:

class Form extends Component {
  handleSubmitClick = () => {
    const name = this._name.value;
    // do something with `name`
  }

  render() {
    return (
      <div>
        <input type="text" ref={input =>this._name = input} />
        <button onClick={this.handleSubmitClick}>Sign up</button>
      </div>
    );
  }
}

换句话说,你要在自己需要的时候从表单域中拉取相应的值。这可能发生在表单提交的时候。

那是实现表单输入的最简单的方法。下面是一些有效的使用场景:在现实世界中的简单表单,还有在学React的时候。

不过它并不是很强大,接下来让我们看看受控表单。

The Controlled

受控表单接收当前值作为属性,或者作为改变其值的调用。可以说它是一种更“React”的方式(但并不意味着总是用它)。

<input value={someValue} onChange={handleChange} />

这很好,但是输入的值不得不保存在某个地方的state里。典型的是,渲染input(又称表单组件)的组件要在其state中保存该值:

class Form extends Component {
  constructor() {
    super();
    this.state = {
      name: '',
    };
  }

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

  render() {
    return (
      <div>
        <input
          type="text"
          value={this.state.name}
          onChange={this.handleNameChange}
        />
      </div>
    );
  }
}

(当然,还可能保存在其他组件的state里,或者一些单独的状态存储像Redux中。)

每次输入新字符时,都会调用handleNameChange。它接收输入的新值,并把它放到state中。

这里写图片描述

  • 首先是空字符串
  • 当你输入a时,handleNameChange会取得a,并调用setState。输入表单的值就会重渲染为a。
  • 你输入了b,handleNameChange获取到ab的值,并把它设置到state中。然后输入域的值就再一次被渲染成ab。

这种方式把值推送给表单组件,因此Form组件总是会有当前输入的值,而不必显示请求。

这意味着你的数据(state)和UI(inputs)总是同步的。状态给输入组件以值 ,用户的输入又促使Form组件改变当前的值。

这也意味着表单组件会立即响应输入的改动,如以下场景:

  • 及时反馈,像验证
  • 使按钮处于不可用状态,除非表单域都有有效值
  • 强制特定输入格式,像信用卡号

但是如果你不需要这些,那就考虑更简单的非受控组件吧。

What makes an element “controlled”

当然有其他表单元素。你可能使用多选框、单选按钮、选择列表和文本域等。

当通过prop设置这些组件的值时,他们就变成了受控组件。

不过,组件使用不同的属性值来设置他们的值,这总结了一个表格:

Element Value property Change callback New value in the callback
<input type="text" /> value="string" onChange event.target.value
<input type="checkbox" /> checked={boolean} onChange event.target.checked
<input type="radio" /> checked={boolean} onChange event.target.checked
<textarea /> value="string" onChange event.target.value
<select /> value="option value" onChange event.target.value

Conclusion

受控和非受控表单域各有各的优势。具体情况具体分析来选择适合自己的方式。

如果你的表单非常简单,只依赖UI反馈,带refs的非受控组件就可以完成的很好,而不用在意那些说这样不好的文章。

feature uncontrolled controlled
one-time value retrieval(e.g. on submit)
validating on submit
instant field validation ×
conditionally disabling submit button ×
enforcing input format ×
sereral inputs for ont piece of data ×
dynamic inputs ×

而且,这并不是一次性决定,你可以总是迁移到受控输入上。从非受控组件迁移到受控组件并不难

最后,这是我组织的关于Reac中表单的文章

References

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章