在上一章中,我们了解了什么是 JSX 以及如何在 JSX 中创建组件。与许多其他框架一样,React 也有其他原型来帮助我们构建 web 应用程序。每个框架都有不同的方式与 DOM 元素交互。React 使用一个快速的内部合成 DOM 来执行差异,并为组件实际所在的位置计算最有效的 DOM 变异。
React 组件类似于采用道具和状态的函数(这将在后面的部分中解释)。React 组件仅渲染单个根节点。如果要渲染多个节点,则必须将它们包装到单个根节点中。
在开始使用表单组件之前,我们应该先看看道具和状态。
React 组件将原始数据转换为丰富的 HTML,道具和状态与原始数据一起构建,以保持 UI 的一致性。
好的,让我们确定它到底是什么:
-
道具和状态都是普通的 JS 对象。
-
它们通过render
更新触发。
-
React 通过调用setState(data,callback)
来管理组件状态。此方法将数据合并到此状态,并重新呈现组件以使 UI 保持最新。例如,下拉菜单的状态(可见或隐藏)。
-
React 不随时间变化的组件道具(属性)。例如,下拉菜单项。有时组件仅使用此props
方法获取一些数据并进行渲染,这会使组件处于无状态。
-
Using props
and state
together helps you make an interactive app.
参考第三章中的这个实例,ReactJS JSX。您将对状态和属性有更好的工作理解。
在本例中,我们将切换(显示或隐藏)的状态和切换按钮的文本作为属性进行管理。
在 React 中,表单组件与其他本地组件不同,因为它们可以通过用户交互进行修改,如<input>
、<textarea>
和<option>
。
以下是支持的事件列表:
onChange
、onInput
和onSubmit
onClick
、onContextMenu
、 onDoubleClick
、onDrag
、onDragEnd
、onDragEnter
和onDragExit
onDragLeave
、onDragOver
、onDragStart
、onDrop
、onMouseDown
、onMouseEnter
和onMouseLeave
onMouseMove
、onMouseOut
、onMouseOver
和onMouseUp
支持的事件的完整列表可在官方文件中找到 https://facebook.github.io/react/docs/events.html#supported-事件。
我们知道,ReactJS 组件有自己的道具和类似状态的表单,它们支持一些通过用户交互影响的道具:
<input>
和<textarea>
| **组件** | **支撑道具** |
| ``和`<textarea>` | 值,默认值 |
| ``复选框或收音机的类型 | 选中,默认选中 |
| `` | 选中,默认值 |
注
在 HTML <textarea>组件中,该值通过子级设置,但在 React 中,该值可以通过value设置。onChange 道具受所有本机组件(如其他 DOM 事件)支持,并且可以侦听所有气泡更改事件。
当用户交互和更改时,onChange道具在浏览器中工作:
<input>和 <textarea>的value
radio和 checkbox的 <input>类型的checked状态
<option>组件的selected状态
在本章中,我们将演示如何使用刚才介绍的属性(prop)和状态来控制组件。然后,我们将看看如何从组件中应用它们来控制行为。
受控元件
我们要看的第一个组件是控制用户输入到textarea的组件,当字符达到最大长度时,它阻止用户输入;当用户输入以下内容时,它还将更新剩余字符:
render: function() {
return <textarea className="form-control" value="fdgdfgd" />;
}
在前面的代码中,我们已经声明了textarea的值,所以当用户输入时,它不会影响textarea的值的更改。为了控制这一点,我们需要使用onChange事件:
var style = {color: "#ffaaaa"};
var max_Char='140';
var Teaxtarea = React.createClass({
getInitialState: function() {
return {value: 'Controlled!!!', char_Left: max_Char};
},
handleChange: function(event) {
var input = event.target.value;
this.setState({value: input});
},
render: function() {
return (
<form>
<div className="form-group">
<label htmlFor="comments">Comments <span style=
{style}>*</span></label>(<span>
{this.state.char_Left}</span> characters left)
<textarea className="form-control" value=
{this.state.value} maxLength={max_Char} onChange=
{this.handleChange} />
</div>
</form>
);
}
})
观察以下屏幕截图:
在前面的屏幕截图中,我们正在接受并控制用户提供的值,并更新<textarea>组件的prop值。
注
this.state()应该只包含表示 UI 状态所需的最小数据量。
但现在我们还要更新<span>中textarea的剩余字符:
this.setState({
value: input.substr(0, max_Char),char_Left: max_Char -
input.length
});
在前面的代码中,this控制textarea的剩余值,并在用户输入时更新剩余字符。
非受控部件
正如我们在 ReactJS 中看到的,当使用value属性时,我们可以控制用户输入,因此没有value属性的 <textarea>是一个不受控制的组件:
render: function() {
return <textarea className="form-control"/>
}
这将使用空值呈现textarea,并且允许用户输入呈现元素立即反映的值,因为非受控组件有其自身的内部状态。如果要初始化默认值,需要使用defaultValue道具:
render:function() {
return <textarea className="form-control" defaultValue="Lorem
lipsum"/>
}
它看起来像是我们以前见过的受控组件。
提交时获取表单值
如我们所见,state和prop将为您提供更改组件值和处理该组件状态的控件。
好的,现在让我们在 addticket 表单中添加一些高级功能,这些功能可以验证用户输入并在 UI 上显示票据。
Ref 属性
React 提供ref非 DOM 属性来访问组件。ref属性可以是一个回调函数,它将在安装组件后立即执行。
因此,我们将在表单元素中附加ref属性以获取值:
var AddTicket = React.createClass({
handleSubmitEvent: function (event) {
event.preventDefault();
console.log("Email--"+this.refs.email.value.trim());
console.log("Issue Type--"+this.refs.issueType.value.trim());
console.log("Department--"+this.refs.department.value.trim());
console.log("Comments--"+this.refs.comment.value.trim());
},
render: function() {
return (
);
}
});
现在,我们将在return方法中添加表单元素的 JSX:
<form onSubmit={this.handleSubmitEvent}>
<div className="form-group">
<label htmlFor="email">Email <span style={style}>*</span>
</label>
<input type="text" id="email" className="form-control"
placeholder="Enter email" required ref="email"/>
</div>
<div className="form-group">
<label htmlFor="issueType">Issue Type <span style={style}>*
</span></label>
<select className="form-control" id="issueType" required
ref="issueType">
<option value="">-----Select----</option>
<option value="Access Related Issue">Access Related
Issue</option>
<option value="Email Related Issues">Email Related
Issues</option>
<option value="Hardware Request">Hardware Request</option>
<option value="Health & Safety">Health & Safety</option>
<option value="Network">Network</option>
<option value="Intranet">Intranet</option>
<option value="Other">Other</option>
</select>
</div>
<div className="form-group">
<label htmlFor="department">Assign Department <span style=
{style}>*</span></label>
<select className="form-control" id="department" required
ref="department">
<option value="">-----Select----</option>
<option value="Admin">Admin</option>
<option value="HR">HR</option>
<option value="IT">IT</option>
<option value="Development">Development</option>
</select>
</div>
<div className="form-group">
<label htmlFor="comments">Comments <span style={style}>*</span>
</label>(<span id="maxlength">200</span> characters left)
<textarea className="form-control" rows="3" id="comments"
required ref="comment"></textarea>
</div>
<div className="btn-group">
<button type="submit" className="btn
btn-primary">Submit</button>
<button type="reset" className="btn btn-link">cancel</button>
</div>
</form>
在前面的代码中,我在表单元素和onSubmit上添加了ref属性,调用函数名handleSubmitEvent。在这个函数中,我们使用this.refs获取值。
现在,打开浏览器,让我们看看代码的输出:
我们正在成功获取组件的值。数据是如何在我们的组件中流动的,这一点非常清楚。在控制台中,当用户单击提交按钮时,我们可以看到表单的值。
现在,让我们在 UI 中显示此票证信息。
首先,我们需要获取表单的值并管理表单的状态:
var AddTicket = React.createClass({
handleSubmitEvent: function (event) {
event.preventDefault();
var values = {
date: new Date(),
email: this.refs.email.value.trim(),
issueType: this.refs.issueType.value.trim(),
department: this.refs.department.value.trim(),
comment: this.refs.comment.value.trim()
};
this.props.addTicketList(values);
},
)};
现在我们将创建 AddTicketForm 组件,该组件将负责管理和保存 addTicketList 的状态(值):
var AddTicketsForm = React.createClass({
getInitialState: function () {
return {
list: {}
};
},
updateList: function (newList) {
this.setState({
list: newList
});
},
addTicketList: function (item) {
var list = this.state.list;
list[item] = item;
//pass the item.id in array if we are using key attribute.
this.updateList(list);
},
render: function () {
var items = this.state.list;
return (
<div className="container">
<div className="row">
<div className="col-sm-6">
<List items={items} />
<AddTicket addTicketList={this.addTicketList} />
</div>
</div>
</div>
);
}
});
让我们看看前面的代码:
getInitialState:初始化 <List />组件的默认状态
addTicketList:保存该值并与状态一起传递到updateList
updateList:用于更新票据列表,使我们的 UI 同步
现在我们需要创建<List items={items} />组件,它在提交表单时迭代列表:
var List = React.createClass({
getListOfIds: function (items) {
return Object.keys(items);
},
createListElements: function (items) {
var item;
return (
this
.getListOfIds(items)
.map(function createListItemElement(itemId) {
item = items[itemId];
return (<ListPanel item={item} />);//key={item.id}
}.bind(this))
.reverse()
);
},
render: function () {
var items = this.props.items;
var listItemElements = this.createListElements(items);
return (
<div className="bg-info">
{listItemElements}
</div>
);
}
});
让我们了解一下前面的代码:
getListOfIds:这将遍历该项中的所有键,并返回我们已与<ListPanel item={item}/>组件映射的列表
.bind(this):this关键字将作为第二个参数传递,该参数在调用函数时给出适当的值
在render方法中,我们只是呈现元素列表。此外,我们还可以根据render方法中的长度添加一个条件:
<p className={listItemElements.length > 0 ? "":"bg-info"}>
{listItemElements.length > 0 ? listItemElements : "You have not
raised any ticket yet. Fill this form to submit the ticket"}
</p>
它将验证长度,并根据返回值 TRUE 或 FALSE 显示消息或应用 Bootstrap 类.bg-info。
现在我们需要创建一个<ListPanel />组件,在 UI 中显示票据列表:
var ListPanel = React.createClass({
render: function () {
var item = this.props.item;
return (
<div className="panel panel-default">
<div className="panel-body">
{item.issueType}<br/>
{item.email}<br/>
{item.comment}
</div>
<div className="panel-footer">
{item.date.toString()}
</div>
</div>
);
}
});
现在,让我们结合我们的代码,在浏览器中查看结果:
var style = {color: "#ffaaaa"};
var AddTicketsForm = React.createClass({
getInitialState: function () {
return {
list: {}
};
},
updateList: function (newList) {
this.setState({
list: newList
});
},
addTicketList: function (item) {
var list = this.state.list;
list[item] = item;
this.updateList(list);
},
render: function () {
var items = this.state.list;
return (
<div className="container">
<div className="row">
<div className="col-sm-12">
<List items={items} />
<AddTicket addTicketList={this.addTicketList} />
</div>
</div>
</div>
);
}
});
//AddTicketsForm components code ends here
var ListPanel = React.createClass({
render: function () {
var item = this.props.item;
return (
<div className="panel panel-default">
<div className="panel-body">
{item.issueType}<br/>
{item.email}<br/>
{item.comment}
</div>
<div className="panel-footer">
{item.date.toString()}
</div>
</div>
);
}
});
// We'll wrap ListPanel component in List
var List = React.createClass({
getListOfIds: function (items) {
return Object.keys(items);
},
createListElements: function (items) {
var item;
return (
this
.getListOfIds(items)
.map(function createListItemElement(itemId) {
item = items[itemId];
return (
<ListPanel item={item} />
);//key={item.id}
}.bind(this))
.reverse()
);
},
render: function () {
var items = this.props.items;
var listItemElements = this.createListElements(items);
return (
<p className={listItemElements.length > 0 ? "":"bg-info"}>
{listItemElements.length > 0 ? listItemElements : "You
have not raised any ticket yet. Fill this form to submit
the ticket"}
</p>
);
}
});
在前面的代码中,我们正在迭代这些项,并作为道具在组件中传递:
var AddTicket = React.createClass({
handleSubmitEvent: function (event) {
event.preventDefault();
var values = {
date: new Date(),
email: this.refs.email.value.trim(),
issueType: this.refs.issueType.value.trim(),
department: this.refs.department.value.trim(),
comment: this.refs.comment.value.trim()
};
this.props.addTicketList(values);
},
render: function() {
return (
// Form template
ReactDOM.render(
<AddTicketsForm />,
document.getElementById('form')
);
以下是我们 HTML 页面的标记:
<link rel="stylesheet" href="css/bootstrap.min.css">
<style type="text/css">
div.bg-info {
padding: 15px;
}
</style>
</head>
<body>
<div class="container">
<div class="row">
<div class="col-sm-6">
<h2>Add Ticket</h2>
<hr/>
</div>
</div>
</div>
<div id="form">
</div>
<script type="text/javascript" src="js/react.js"></script>
<script type="text/javascript" src="js/react-dom.js"></script>
<script src="js/browser.min.js"></script>
<script src="component/advance-form.js" type="text/babel"></script>
</body>
在提交之前,打开浏览器,让我们查看表单的输出:
以下屏幕截图显示了提交表单后的外观:
这看起来不错。我们的第一个全功能 React 组件已准备就绪。
注
不要访问任何组件内部的refs,也不要将它们附加到无状态函数。
观察以下屏幕截图:
我们收到此警告消息是因为 React 的key(可选)属性接受唯一 ID。每次提交表单时,它都会迭代List组件以更新 UI。例如:
createListElements: function (items) {
var item;
return (
this
.getListOfIds(items)
.map(function createListItemElement(itemId,id) {
item = items[itemId];
return (<ListPanel key={id} item={item} />);
}.bind(this))
.reverse()
);
},
React 提供附加模块来解决此类警告并生成唯一 ID,但它仅在 npm 中可用。在接下来的章节中,我们将展示如何使用 React npm 模块。以下是一些常用加载项的列表:
TransitionGroup和CSSTransitionGroup:用于处理动画和过渡
LinkedStateMixin:方便与用户表单输入数据和组件状态交互
cloneWithProps:更改组件的道具并进行浅拷贝
createFragment:用于创建一组外部键控的子项
Update:一个帮助函数,可以轻松处理 JavaScript 中的数据
PureRenderMixin:性能助推器
shallowCompare:一个辅助函数,用于对道具和状态进行粗略比较
Bootstrap 助手类
Bootstrap 提供了一些帮助器类,为您提供更好的用户体验。在AddTicketsForm表单组件中,我们使用了 Bootstrap 助手类*-info,它可以帮助您用颜色向屏幕阅读器传达消息的含义。其中一些是*-muted、*-primary、*-success、*-info、*-warning和*-danger。
要更改文本的颜色,我们可以使用.text*:
<p class="text-info">...</p>
要更改背景色,我们可以使用.bg*:
<p class="bg-info">...</p>
插入符号
要显示指示下拉菜单方向的插入符号,我们可以使用:
<span class="caret"></span>
Clearfix
在父元素上使用clearfix可以清除子元素的浮动:
<div class="clearfix">...
<div class="pull-left"></div>
<div class="pull-right"></div>
</div>
总结
在本章中,我们看到了道具和状态如何在组件交互以及 DOM 交互中发挥重要作用。Refs 是与 DOM 元素交互的好方法。这将不方便通过流式 React 道具和状态来实现。在 refs 的帮助下,我们可以调用任何公共方法并向特定的子实例发送消息。
本章中显示的关键示例将帮助您理解和明确有关道具、状态和 DOM 交互的概念。
最后一个示例介绍了带有多个 JSX 组件和 Bootstrap 的高级添加票证表单,这将为您提供有关创建 React 组件以及如何使用 REF 与它们交互的更多想法。您可以使用它,并像使用 HTML 一样轻松地使用它。
如果您仍然不确定状态和道具是如何工作的,以及 React 如何与 DOM 交互的,我建议您再次阅读本章,这也将有助于您了解未来的章节。
如果您已经完成了,那么让我们继续看第 5 章,*jQuery Bootstrap 组件和 React,*都是关于 React 中的 Redux 架构的。