"小和山的菜鸟们",为前端开发者提供技术相关资讯以及系列基础文章。为更好的用户体验,请您移至我们官网小和山的菜鸟们 ( https://xhs-rookies.com/ ) 进行学习,及时获取最新文章。
"Code tailor" ,如果您对我们文章感兴趣、或是想提一些建议,微信关注 “小和山的菜鸟们” 公众号,与我们取的联系,您也可以在微信上观看我们的文章。每一个建议或是赞同都是对我们极大的鼓励!
这节我们将介绍 React
中受控组件和非受控组件的概念及其使用。
本文会向你介绍以下内容:
在 HTML
中,表单元素如 <input>
,<textarea>
和 <select>
表单元素通常保持自己的状态,并根据用户输入进行更新。而在 React
中,可变状态一般保存在组件的 state(状态)
属性中,并且只能通过 setState()
更新。
我们可以通过使 React
的 state
成为 “单一数据源原则” 来结合这两个形式。然后渲染表单的 React 组件也可以控制在用户输入之后的行为。
这种形式,其值由 React
控制的输入表单元素称为“受控组件”。
那么相反的,值并不由 React
进行控制,该组件自己输入,减少等等,该元素成为非受控组件。
关于什么时候使用受控组件,什么时候使用非受控组件,可以查看这一篇文章:
HTML
表单元素与 React
中的其他 DOM
元素有所不同,因为表单元素自然地保留了一些内部状态。
例如,这个纯 HTML
表单接受一个单独的 name
:
<form>
<label>
名字:
<input type="text" name="name" />
</label>
<input type="submit" value="提交" />
</form>
该表单和 HTML
表单的默认行为一致,当用户提交此表单时浏览器会打开一个新页面。如果你希望 React
中保持这个行为,也可以工作。
但是多数情况下,我们会让 React
组件来管理这些数据,并在点击提交这些数据并触发打开新页面的操作。
这就用到了“受控组件(controlled components
)”。
在 HTML
中,表单元素(如<input>
、 <textarea>
和 <select>
)之类的表单元素通常自己维护 state,并根据用户输入进行更新。
而在 React
中,可变状态(mutable state
)通常保存在组件的 state 属性中,并且只能通过使用 setState()
来更新。
React
的 state
成为“唯一数据源”;React
组件还控制着用户输入过程中表单发生的操作;React
以这种方式控制取值的表单输入元素就叫做“受控组件”;例如,如果我们想让前一个示例在提交时打印出名称,我们可以将表单写为受控组件:
class App extends PureComponent {
constructor(props) {
super(props)
this.state = {
username: '',
}
}
render() {
const { username } = this.state
return (
<div>
<form onSubmit={(e) => this.handleSubmit(e)}>
<label htmlFor="username">
用户名:
<input
type="text"
id="username"
onChange={(e) => this.handleUsernameChange(e)}
value={username}
/>
</label>
<input type="submit" value="提交" />
</form>
</div>
)
}
handleUsernameChange(event) {
this.setState({
username: event.target.value,
})
}
handleSubmit(event) {
console.log(this.state.username)
event.preventDefault()
}
}
由于在表单元素上设置了 value
属性,因此显示的值将始终为 this.state.value
,这使得 React 的 state 成为唯一数据源。
由于 handleUsernameChange
在每次按键时都会执行并更新 React
的 state
,因此显示的值将随着用户输入而更新。
在 React
的开发模式中,通常情况下不需要、也不建议直接操作 DOM
原生,但是某些特殊的情况,确实需要获取到 DOM
进行某些操作:
DOM
库。我们这里如果使用非受控组件就会有一个问题,如何获取该组件的数据,这里就可以用 Refs
来获取该组件,然后就可以获取到该组件的数据了。因此我们先简述 Refs
的内容。
注意:当然还有其他的方法可以获取组件内容,例如:变量提升到父组件统一管理、事件监听。
如何创建 refs
来获取对应的 DOM
呢?目前有三种方式:
使用时通过 this.refs.传入的字符串
格式获取对应的元素;
对象是通过 React.createRef()
方式创建出来的;使用时获取到创建的对象其中有一个current
属性就是对应的元素;
该函数会在 DOM
被挂载时进行回调,这个函数会传入一个 元素对象,我们可以自己保存;使用时,直接拿到之前保存的元素对象即可;
代码演练:
class App extends PureComponent {
constructor(props) {
super(props)
this.titleRef = createRef()
this.titleEl = null
}
render() {
return (
<div>
<h2 ref="title">String Ref</h2>
<h2 ref={this.titleRef}>Hello Create Ref</h2>
<h2 ref={(element) => (this.titleEl = element)}>Callback Ref</h2>
<button onClick={(e) => this.changeText()}>改变文本</button>
</div>
)
}
changeText() {
this.refs.title.innerHTML = '你好啊,小和山的菜鸟们'
this.titleRef.current.innerHTML = '你好啊,小和山的菜鸟们'
this.titleEl.innerHTML = '你好啊,小和山的菜鸟们'
}
}
ref
的值根据节点的类型而有所不同:
ref
属性用于 HTML
元素时,构造函数中使用 React.createRef()
创建的 ref
接收底层 DOM
元素作为其 current
属性;ref
属性用于自定义 class
组件时,ref
对象接收组件的挂载实例作为其 current
属性;ref
属性,因为他们没有实例;这里我们演示一下 ref
引用一个 class
组件对象:
class Counter extends PureComponent {
constructor(props) {
super(props)
this.state = {
counter: 0,
}
}
render() {
return (
<div>
<h2>当前计数: {this.state.counter}</h2>
<button onClick={(e) => this.increment()}>+1</button>
</div>
)
}
increment() {
this.setState({
counter: this.state.counter + 1,
})
}
}
class App extends PureComponent {
constructor(props) {
super(props)
this.counterRef = createRef()
}
render() {
return (
<div>
<Counter ref={this.counterRef} />
<button onClick={(e) => this.increment()}>app +1</button>
</div>
)
}
increment() {
this.counterRef.current.increment()
}
}
函数式组件是没有实例的,所以无法通过 ref
获取他们的实例,但是某些时候,我们可能想要获取函数式组件中的某个 DOM
元素,这个时候我们可以通过 React.forwardRef
,后面我们也会学习 hooks
中如何使用 ref
。
React
推荐大多数情况下使用受控组件来处理表单数据:
React
组件来管理的;DOM
节点来处理;如果要使用非受控组件中的数据,那么我们需要使用 Ref
来从 DOM
节点中获取表单数据。
我们来进行一个简单的演练:
ref
来获取 input
元素;defaultValue
来设置默认值;class App extends PureComponent {
constructor(props) {
super(props)
this.usernameRef = createRef()
}
render() {
return (
<div>
<form onSubmit={(e) => this.handleSubmit(e)}>
<label htmlFor="">
用户:
<input defaultValue="username" type="text" name="username" ref={this.usernameRef} />
</label>
<input type="submit" value="提交" />
</form>
</div>
)
}
handleSubmit(event) {
event.preventDefault()
console.log(this.usernameRef.current.value)
}
}
同样,<input type="checkbox">
和 <input type="radio">
支持 defaultChecked
,<select>
和 <textarea>
支持 defaultValue
。
本节我们学习了 React
中控组件和非受控组件的内容,在下一个章节我们将继续学习 React
中高阶组件以及组件补充的内容,敬请期待!
而且,很多时候,维护需要你的网站至少断网几分钟。 那么,在你的网站将要关机维...
原理 主站点内嵌代理页面, 并向代理页传递数据, 代理页根据主站点的数据对目标页...
每到开学季,大学生从全国各地汇聚于一所学校,而迎接新生们的除了学长学姐,还...
作者:小傅哥 博客: https://bugstack.cn 沉淀、分享、成长,让自己和他人都能...
dreamweaver是一款网页设计软件,可以通过设置按钮和写很少的代码实现想要的页面...
bid 域名 需要实名么?是需要实名的。因为,只要是在国内注册的 域名 ,均需做实...
互联网改革了我们生活的方方面面,包括传统的面对面教育模式。随着线上沟通工具...
TOP云 (west.cn)6月16日消息,近日一个组合 域名 NETCO.COM在国外平台以10,200...
前言:文章的灵感来源于,社群中某大佬分享一个自己耗时数月维护的github项目 aw...
之前在这篇文章中 -- 《老生常谈之 CSS 实现三角形》 ,介绍了 6 种使用 CSS 实...