受控组件
受控组件是渲染表单的组件,但表单状态的来源位于组件状态内,而非DOM内,Reactd在控制表单的状态,这样做的好处在于:
一、它们支持即时输入验证;二、它们允许你有条件地禁用或启用表单按钮;三、它们限制了输入的格式。
代码示例:
class NameForm extends React.Component{
state = {
email:''
}
handleChange = (event) => {
this.setState({email:event.target.value})
}
render(){
return (
<form>
<input type="text" value = {this.state.email}
onChange = {this.handleChange}
\>
</form>
)
}
}
React 开发者工具
在构建 React 应用时,有时候很难知道组件内部的发生情况。毕竟传递和访问了太多的属性,并嵌套了无数的组件,以及还有所有的 JSX 尚需渲染为 HTML,有时候很难让一切井然有序!
React 开发者工具
使你能够检查组件的层次结构以及各自的属性和状态。安装该 Chrome 扩展程序后,打开 Chrome 控制台并查看 React
标签页。
受控组件示例:
/**
* Created by yons on 2018/3/8.
*/
import React ,{Component}from 'react'
import PropsTypes from 'prop-types';
class ListContacts extends React.Component{
//添加静态propTypes属性
static propTypes = {
contacts:PropsTypes.array.isRequired,
onDeleteContact:PropsTypes.func.isRequired
}
state = {
query:'',
}
//
updateQuery = (state) => {
this.setState({
//去除state状态的前后空格
query:state.trim(),
});
}
render(){
return (
<div className="list-contacts">
{/* 受控组件input */}
<div className="list-contacts-top">
{JSON.stringify(this.state)}
<input
className="search-contacts"
type="text"
value={this.state.query}
//监听onChange事件,event的target的值传输给state更新状态
onChange={(event) => this.updateQuery(event.target.value)}
/>
</div>
{/* 无状态组件 有序列表ol ul是无序列表 */}
<ol className="contact-list">
{this.props.contacts.map((contact)=>(
<li key={contact.id} className="contact-list-item">
<div className ="contact-avatar" style={{
backgroundImage:`url(${contact.avatarURL})`
}}/>
<div className='contact-details'>
<p>{ contact.name } </p>
<p>{ contact.email } </p>
</div>
{/* onDeleteContact函数中的contact参数代表,当前正在迭代的contact联系人 */}
<button onClick={() => this.props.onDeleteContact(contact)} className="contact-remove">
remove
</button>
</li>
))}
</ol>
</div>
);
}
}
//输出组件
export default ListContacts
注意 value
属性在 <input>
元素上设置。我们显示的值将始终为组件的状态
中的值,使状态
成为“单一数据源”。
因为是 React 最终控制输入表格元素的值
,因此我们将该组件视为受控组件|(翻译)。
总结下用户输入如何影响到 ListContacts
组件自己的状态
:
- 用户在输入字段中输入文本。
- 事件监听器在每个
onChange
事件上触发updateQuery()
函数。 -
updateQuery()
然后调用setState()
,合并新状态以更新组件的内部状态。 - 因为
ListContacts
的状态已变化,所以它会重新渲染。
我们看看如何利用这个更新后的状态
过滤通讯录!为了帮助我们过滤通讯录,我们需要以下软件包:
npm install --save escape-string-regexp sort-by
💡 正则表达式 💡
在上个视频中,我们创建了一个正则表达式对象并使用该对象检测通讯录姓名的格式。正则表达式太复杂,这节课根本无法深入介绍,但是它们在验证格式方面作用非常大。
请参阅 MDN 对正则表达式的介绍。此外,请参阅 String .match()
方法如何使用正则表达式验证文本格式。
目前,我们的组件有点混乱;render()
方法经常从状态对象访问 query
(this.state.query
),并从属性对象访问 contacts
(this.props.contacts
)。因为属性和状态只是 JavaScript 对象,我们可以使用 ES6 功能将它们拆分为单个变量,而不是每次都将它们当做 this.state.query
和 this.props.contacts
。这种拆分流程叫做对象解构。
总之,解构对象不会改变代码的返回值,但是可以让内容看起来更清晰。
受控组件总结
受控组件是指渲染表格的组件,但是该表格状态的“数据源”位于组件状态里,而不是 DOM 里。受控组件的优势包括:
立即验证输入
有条件地停用/启用按钮
控制输入格式
在 ListContacts 组件中,该组件不仅会渲染表格,而且根据用户输入控制该表格中发生的情况。在这种情况下,事件处理器使用用户的搜索查询更新组件状态。我们已经知道,对 React 状态的任何更改都将导致重新渲染页面内容,并有效地显示实时搜索结果。
案例代码:
/**
* Created by yons on 2018/3/8.
*/
import React ,{Component}from 'react'
import PropsTypes from 'prop-types';
import escapeRegExp from 'escape-string-regexp';
import sortBy from 'sort-by';
class ListContacts extends React.Component{
//添加静态propTypes属性
static propTypes = {
contacts:PropsTypes.array.isRequired,
onDeleteContact:PropsTypes.func.isRequired
}
state = {
query:'',
}
updateQuery = (state) => {
this.setState({
//去除state状态的前后空格
query:state.trim(),
});
}
/*通过清空query的值来显示全部联系人*/
clearQuery = () => {
this.setState({
query:''
})
}
render(){
/*es6 简化props和state属性*/
const {contacts,onDeleteContact} = this.props;
const {query} = this.state;
/*创建经过过滤的数组,给下面的组件遍历*/
let showingContacts;
if(query){
/*创建正则表达式,并用escapeRegExp过滤掉特殊字符,i代表勿略大小写*/
const match = new RegExp(escapeRegExp(query),'i');
/*用test方法来验证,contacts数组中name字段是否配备到this.state.query的值,用到filter方法过滤数组*/
showingContacts = contacts.filter((contact) => match.test(contact.name));
}else{
showingContacts = contacts;
}
/*名字按字母的顺序排下序*/
showingContacts.sort(sortBy('name'));
return (
<div className="list-contacts">
{/* 受控组件input */}
<div className="list-contacts-top">
{/*{JSON.stringify(this.state)}*/}
<input
className="search-contacts"
type="text"
value={query}
//监听onChange事件,event的target的值传输给state更新状态
onChange={(event) => this.updateQuery(event.target.value)}
/>
</div>
{/*跟据状态来改变UI*/}
{showingContacts.length !== contacts.length && (
<div className="showing-contacts">
<span>
now showing {showingContacts.length} of {contacts.length} total
</span>
{/* 添加按钮清空输入 绑定clearQuery函数到onClick事件*/}
<button onClick={this.clearQuery}>显示全部</button>
</div>
)}
{/* 无状态组件 有序列表ol ul是无序列表 */}
<ol className="contact-list">
{showingContacts.map((contact)=>(
<li key={contact.id} className="contact-list-item">
<div className ="contact-avatar" style={{
backgroundImage:`url(${contact.avatarURL})`
}}/>
<div className='contact-details'>
<p>{ contact.name } </p>
<p>{ contact.email } </p>
</div>
{/* onDeleteContact函数中的contact参数代表,当前正在迭代的contact联系人 */}
<button onClick={() => onDeleteContact(contact)} className="contact-remove">
remove
</button>
</li>
))}
</ol>
</div>
);
}
}
//输出组件
export default ListContacts
汇总所有信息
在跟踪应用数据时,思考下需要如何处理该数据,并且当用户查看应用界面时,数据的外观看起来如何。如果你希望组件存储可变的本地数据,可以考虑使用状态存储该信息。很多时候,系统会使用状态来管理组件中的受控表格元素。
另一方面,如果某些信息一直不会变化,在整个应用中基本上是“只读”状态,可以考虑改为使用属性。状态和属性通常都是对象形式,更改任何一个都会触发重新渲染组件,但是它们各自在应用中扮演了截然不同的角色。
在这节课,我们讲解了很多内容,并且你学到了很多知识。以下是可以帮助你巩固这些概念的课外资料: