如何从jQuery到React.js?

我一直在读React了几天。 我可以理解我所看到的大部分内容,但是我并不完全相信自己能够编写它。 我一直在做一个小型的networking应用程序,通过jQuery完成所有的html代码,并将元素附加到对方。 我想尝试用React重build,因为我相信它会更快。 这个JSFiddle是我正在处理的事情的一个小例子。 你会怎样用React写它?

JS:

function remove() { this.remove(); } function timed_append_new_element() { setTimeout(function () { var added_content = $("<span />", { class: "added_content", text: "Click to close", click: remove }); container.append(added_content); }, 3000); } function append_new_element() { var added_content = $("<span />", { class: "timed_added_content", text: "Click to close", click: remove }); container.append(added_content); } var container = $("<div />", { class: "container" }); var header = $("<span />", { class: "header", text: "jQuery to React.js Header" }); var add_button = $("<button />", { class: "add_button", text: "Add Element", click: append_new_element }); var timed_add_button = $("<button />", { class: "add_button", text: "Add Element in 3 seconds", click: timed_append_new_element }); container.append(header); container.append(add_button); container.append(timed_add_button); $("body").append(container); 

有几个基本的原则要记住,可以帮助你build立一个良好的React应用程序:

你的用户界面应该是数据的一个function

在许多“jQuery汤”风格的应用程序中,应用程序的业务逻辑,应用程序的数据以及UI交互代码都混杂在一起。 这使得这些应用程序很难debugging,特别是难以发展。 与许多现代的客户端应用程序框架一样,React强制实现UI只是数据表示的思想。 如果你想要改变你的用户界面,你应该改变一些数据,并允许框架用来为你更新界面的任何绑定系统。

在React中,每个组件(理想情况下)都是两个数据的函数 – 传递给组件实例的属性以及组件在内部pipe理的状态 。 给定相同的属性(或“道具”)和状态,组件应该以相同的方式呈现。

如果没有具体的例子,这可能是一个抽象的概念,所以在我们继续前进的时候记住它。

不要触摸DOM

在React中,甚至比其他数据绑定框架更好,如果可能的话,你应该尽量不要直接操纵DOM。 React的许多性能和复杂性特性都是可能的,因为React在内部使用虚拟DOM和差异algorithm来操作真实的DOM。 任何时候,当你构build一个伸手可及的DOM组件时,你应该问自己,是否可以用React的虚拟DOMfunction更通俗地构build相同的function。

当然,有时候你需要访问DOM,或者你想要合并一些jQuery插件,而不用在React中重build它。 对于这样的时代,React为您提供了良好的组件生命周期钩子 ,您可以使用它来确保React的性能不会受到太大的影响(或者在某些情况下,不要让组件受到明显的中断)。

不操纵DOM与上面的“UI作为数据的function”是一致的。

反转数据stream

在一个大型的React应用程序中,可能很难跟踪哪个子组件pipe理某个应用程序数据。 出于这个原因,React小组build议将数据操作逻辑保存在一个中央位置。 最直接的方法是将callback传递给子组件; 在Facebook上也有一个名为Flux的架构,它有自己的网站 。

创build可组合的组件

很多时候,编写一个pipe理好几个状态或几个UI逻辑的大型组件是很有诱惑力的。 在可能的情况下(在合理范围内),您应该考虑将更大的组件分解为更小的组件,这些组件可以在单个数据或UI逻辑上运行。 这使得扩展和移动应用程序块变得更容易。

谨防可变数据

由于组件状态只能通过从组件内部调用this.setState来更新,因此谨慎对待可变数据是有帮助的。 当多个函数(或组件!)可能在同一个tick中更新可变对象时,这是双重的; React可能会尝试批量更改状态,并且可能会丢失更新! 正如Eliseu Monar的评论中所提到的那样,考虑在变异之前克隆可变对象。 React有可以协助的不变性助手 。

另一个select是放弃保持可变数据结构直接处于状态; 上面提到的Flux模式是一个有趣的想法。


React网站上有一篇名为“ Thinking in React”的文章,讲述了如何创build一个想法或模型,并将其转化为一个React应用程序,我强烈build议您仔细阅读。 作为一个具体的例子,我们来看看你提供的代码。 你基本上有一块数据要pipe理: container元素内存在的内容列表。 对UI的所有更改都可以通过添加,删除和对该数据的更改来表示。

通过应用上面的原则,你的最终应用可能看起来像这样:

 /** @jsx React.DOM */ var Application = React.createClass({ getInitialState: function() { return { content: [] }; }, render: function() { return ( <div className="container"> <span className="header">jQuery to React.js Header</span> <button className="add_button" onClick={this.addContent}>Add Element</button> <button className="add_button" onClick={this.timedAddContent}>Add Element in 3 Seconds</button> {this.state.content.map(function(content) { return <ContentItem content={content} removeItem={this.removeItem} />; }.bind(this))} </div> ); }, addContent: function() { var newItem = {className: "added_content", text: "Click to close"}, content = this.state.content, newContent = React.addons.update(content, {$push: [newItem]}); this.setState({content: newContent}); }, timedAddContent: function() { setTimeout(function() { var newItem = {className: "timed_added_content", text: "Click to close"}, content = this.state.content, newContent = React.addons.update(content, {$push: [newItem]}); this.setState({content: newContent}); }.bind(this), 3000); }, removeItem: function(item) { var content = this.state.content, index = content.indexOf(item); if (index > -1) { var newContent = React.addons.update(content, {$splice: [[index, 1]]}); this.setState({content: newContent}); } } }); var ContentItem = React.createClass({ propTypes: { content: React.PropTypes.object.isRequired, removeItem: React.PropTypes.func.isRequired }, render: function() { return <span className={this.props.content.className} onClick={this.onRemove}>{this.props.content.text}</span>; }, onRemove: function() { this.props.removeItem(this.props.content); } }); React.renderComponent(<Application />, document.body); 

您可以在此JSFiddle中看到正在执行的代码: http : //jsfiddle.net/BinaryMuse/D59yP/

该应用程序由两部分组成:一个名为Application的顶级组件,它pipe理(在其状态中)一个名为content的数组,以及一个名为ContentItem的组件,它表示该数组中单个项的UI和行为。 Applicationrender方法为内容数组中的每个项目返回一个ContentItem元素。

有一点要注意的是,pipe理content数组内部值的所有逻辑都是在Application组件中处理的; ContentItem组件传递一个对ApplicationremoveItem方法的引用 ,该方法是ContentItem在单击时委托的。 这保持了在顶层组件中操纵状态的所有逻辑。