需要牢记一些基本原则,这些原则可以帮助您构建良好的React应用程序:
您的UI应该是数据的函数
在许多“ jQuery汤”式的应用程序中,应用程序的业务逻辑,应用程序的数据和UI交互代码都是混杂在一起的。这使得这类应用程序难以调试,尤其是难以增长。像许多现代的客户端应用程序框架一样,React强制执行以下想法:UI只是数据的表示。如果要更改UI,则应更改一条数据,并允许框架使用任何绑定系统为您更新UI。
在React中,每个组件(理想情况下)是两个数据的函数-传递给组件实例的属性以及组件内部管理的状态。给定相同的属性(或“属性”)和状态,组件应以相同的方式呈现。
如果没有具体示例,这可能是一个抽象的想法,因此在继续进行操作时请牢记这一点。
不要碰DOM
在React中,甚至比其他数据绑定框架更重要的是,您应该尽可能不要直接操纵DOM。由于React使用内部带有差异算法的虚拟DOM来对实际DOM进行操作,因此许多React的性能和复杂性特性都是可能的。每当您构建一个可以伸出并执行自己的DOM操作的组件时,您都应该问自己是否可以使用React的虚拟DOM功能更惯用地构建相同的功能。
当然,有时您需要访问DOM,或者想要合并一些jQuery插件而不在React中重建它。在这种情况下,React为您提供了良好的组件生命周期挂钩,您可以使用它们来确保React的性能不会受到太大的影响(或者在某些情况下,可以防止组件遭到破坏)。
不操纵DOM与上面的“ UI作为数据的函数”紧密相关。
反转数据流
在大型React应用程序中,很难跟踪哪个子组件正在管理某些应用程序数据。因此,React团队建议将数据操作逻辑保持在中央位置。最简单的方法是将回调传递到子组件中。Facebook还开发了一种名为Flux的架构,该架构拥有自己的网站。
创建可组合的组件
很多时候,编写一个大型组件来管理多个状态或多个UI逻辑可能很诱人。在可能的情况下(并且在合理的范围内),您应该考虑将较大的组件分解为可对单个数据或UI逻辑进行操作的较小的组件。这使得扩展和在应用程序的各个部分之间移动变得容易得多。
当心可变数据
由于仅应通过this.setState
从组件内部调用来更新组件状态,因此请警惕可变数据。当多个功能(或组件!)可能在同一时刻更新可变对象时,情况就更是如此。React可能会尝试批处理状态更改,并且您可能会丢失更新!如Eliseu Monar的评论中所述,请考虑在对可变对象进行突变之前对其进行克隆。React具有可以提供帮助的不变性助手。
另一种选择是完全放弃直接保持可变数据结构的状态。上面提到的助焊剂模式是对此想法的有趣理解。
在React网站上有一篇很棒的文章,叫做《Reacting in React》,探讨了如何将一个想法或一个模型转化为一个React应用程序,我强烈建议您仔细研究一下。作为一个具体的例子,让我们看一下您提供的代码。实际上,您需要管理一个数据:container
元素内部存在的内容列表。用户界面的所有更改都可以通过对数据的添加,删除和更改来表示。
通过应用上述原则,您的最终应用程序可能看起来像这样:
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
,该组件管理(处于其状态)一个名为的数组content
,一个名为的组件ContentItem
,该组件代表该数组中单个项目的用户界面和行为。Application
的render
方法ContentItem
为content数组中的每个项目返回一个元素。
需要注意的一件事是,用于管理content
数组内部值的所有逻辑都是在Application
组件中处理的。该ContentItem
成分被送到一个参考到Application
的removeItem
方法,其中所述ContentItem
点击时委托给。这将所有用于操纵状态的逻辑保留在顶级组件内。