Skip to content

Latest commit

 

History

History
612 lines (484 loc) · 21.2 KB

File metadata and controls

612 lines (484 loc) · 21.2 KB

四、DOM 与 ReactJS 的交互

在上一章中,我们了解了什么是 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.

    Props and state

参考第三章中的这个实例,ReactJS JSX。您将对状态和属性有更好的工作理解。

在本例中,我们将切换(显示或隐藏)的状态和切换按钮的文本作为属性进行管理。

表单组件

在 React 中,表单组件与其他本地组件不同,因为它们可以通过用户交互进行修改,如<input><textarea><option>

以下是支持的事件列表:

  • onChangeonInputonSubmit
  • onClickonContextMenuonDoubleClickonDragonDragEndonDragEnteronDragExit
  • onDragLeaveonDragOveronDragStartonDroponMouseDownonMouseEnteronMouseLeave
  • onMouseMoveonMouseOutonMouseOveronMouseUp

支持的事件的完整列表可在官方文件中找到 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 架构的。