如何从jQuery转到React.js?
我已经读了几天React了.我可以理解大部分的内容,但是我对自己的编写能力并不完全自信.我一直在研究一个小型Web应用程序,该应用程序通过jQuery完成所有html生成并将元素彼此附加.我想尝试用React重建它,因为我相信它会更快.这个 JSFiddle 是我正在从事的这类工作的一个小例子.您将如何使用React编写它?
I have been reading up on React for a few days nows. I can understand most of what I'm looking at, but I'm not entirely confident in my ability to write it. I have been working on a small web app that does all of its html generation through jQuery and appending elements to each other. I'd like to try and rebuild this with React because I believe that it would be faster. This JSFiddle is a small example of the sort of thing I am working on. How would you write it with 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);
需要牢记一些基本原则,这些原则可以帮助您构建良好的React应用程序:
There are a few basic tenets to keep in mind that may help you build a good React application:
您的UI应该是数据的函数
在许多"jQuery汤"式的应用程序中,应用程序的业务逻辑,应用程序的数据和UI交互代码都混合在一起.这使得这类应用程序难以调试,尤其是难以增长.像许多现代的客户端应用程序框架一样,React强制实现了UI只是数据表示的想法.如果要更改UI,则应更改一条数据,并允许框架使用任何绑定系统为您更新UI.
In many "jQuery soup" style applications, the business logic for the application, the app's data, and the UI interaction code are all intermingled. This makes these sorts of applications difficult to debug and, especially, difficult to grow. React, like many modern client-side application frameworks, enforce the idea that the UI is just a representation of your data. If you want your UI to change, you should change a piece of data and allow whatever binding system the framework uses to update the UI for you.
在React中,每个组件(理想情况下)是两个数据的函数-传递给组件实例的 properties 和组件内部管理的 state .在具有相同的属性(或道具")和状态的情况下,组件应以相同的方式呈现.
In React, each component is (ideally) a function of two pieces of data–the properties passed to the component instance, and the state that the component manages internally. Given the same properties (or "props") and state, the component should render in the same way.
这可能是一个抽象的想法,没有具体的示例,因此请在继续进行操作时牢记.
This can be a bit of an abstract idea without concrete examples, so keep it in mind as we move on for now.
请勿触摸DOM
在React中,比其他数据绑定框架更重要的是,您应该尽可能不要直接操纵DOM.由于React使用内部带有差异算法的虚拟DOM来对实际DOM进行操作,因此许多React的性能和复杂性特性都是可能的.每当您构建一个可以伸出并执行自己的DOM操作的组件时,都应该问自己是否可以使用React的虚拟DOM功能更惯用地构建相同的功能.
In React, even more so than other data-bound frameworks, you should try not to manipulate the DOM directly if at all possible. A lot of React's performance and complexity characteristics are only possible because React uses a virtual DOM with diffing algorithms internally to operate on the real DOM. Any time you build a component that reaches out and does its own DOM manipulation, you should ask yourself if you could build the same feature more idiomatically with React's virtual DOM features.
当然,有时您需要访问DOM,或者想要合并一些jQuery插件而不在React中重新构建它.在这种情况下,React为您提供了很好的组件生命周期挂钩,您可以使用它们来确保React的性能不会受到太大的影响(或者在某些情况下,可以防止组件受到破坏).
Of course, sometimes you'll need to access the DOM, or you'll want to incorporate some jQuery plugin without rebuilding it in React. For times like these, React gives you good component lifecycle hooks that you can use to ensure that React's performance doesn't suffer too much (or, in some cases, to keep your component from plain breaking).
不操作DOM与上面的"UI作为数据的函数"是紧密结合的.
Not manipulating the DOM goes hand-in-hand with "UI as a function of the data," above.
反转数据流
在大型React应用程序中,可能很难跟踪哪个子组件正在管理某些应用程序数据.因此,React团队建议将数据操作逻辑保持在中央位置.最简单的方法是将回调传递到子组件中.还有一种在Facebook开发的名为Flux的架构,该架构具有自己的网站.
In a large React application, it can be difficult to keep track of which sub-component is managing a certain piece of application data. For this reason, the React team recommends keeping data manipulation logic in a central location. The most straightforward way to do this is to pass callbacks into child components; there's also an architecture developed at Facebook called Flux which has its own website.
创建可组合的组件
很多时候,编写一个大型组件来管理多个状态或多个UI逻辑可能很诱人.在可能的情况下(并且在合理的范围内),您应该考虑将较大的组件分解为较小的组件,这些组件可以对单个数据或UI逻辑进行操作.这样可以更轻松地扩展和移动应用程序的各个部分.
A lot of times, it can be tempting to write a large component that manages several pieces of state or several pieces of UI logic. Where possible (and within reason), you should consider breaking larger components into smaller ones that operate on a single piece of data or UI logic. This makes it much easier to extend and move around pieces of your application.
当心可变数据
由于仅应通过从组件内部调用this.setState
来更新组件状态,因此请警惕可变数据.当多个函数(或组件!)可能在同一时刻更新可变对象时,这是双重事实. React可能会尝试批处理状态更改,并且您可能会丢失更新!正如Eliseu Monar的评论中所提到的,考虑在对可变对象进行突变之前先对其进行克隆. React具有不可变性帮助程序可以提供帮助.
Since component state should only be updated via calls to this.setState
from within the component, it's helpful to be wary of mutable data. This is doubly true when multiple functions (or components!) might update the mutable object in the same tick; React might try to batch state changes, and you could lose updates! As mentioned in the comments by Eliseu Monar, consider cloning mutable objects before mutating them. React has immutability helpers that can assist.
另一种选择是完全放弃将可变数据结构直接保持在状态中.上面提到的助焊剂"模式对这个想法很有趣.
Another option is to forgo keeping mutable data structures directly in state at all; the Flux pattern, mentioned above, is an interesting take on this idea.
React网站上有一篇很棒的文章,名为 React的思考,您如何考虑一个想法或一个模型,然后将其转变为一个React应用程序,我强烈建议您仔细研究一下.作为一个具体的例子,让我们看一下您提供的代码.本质上,您需要管理一个数据:container
元素中存在的内容列表.用户界面的所有更改都可以通过对该数据的添加,删除和更改来表示.
There's a great article on the React site called Thinking in React which goes over how you might take an idea or a mockup and turn it into a React application, and I strongly encourage going over it. As a concrete example, let's take a look at the code you provided. You essentially have one piece of data to manage: a list of content that exists inside the container
element. All the changes to your UI can be represented by additions, removals, and changes to that data.
通过应用上述原则,您的最终应用程序可能看起来像这样:
By applying the tenets above, your final application might look something like this:
/** @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 中看到运行中的代码:
You can see the code in action in this JSFiddle: http://jsfiddle.net/BinaryMuse/D59yP/
该应用程序由两个组件组成:一个名为Application
的顶级组件,该组件管理(处于其状态)一个名为content
的数组,以及一个名为ContentItem
的组件,该组件表示UI的用户界面和行为该数组中的单个项目. Application
的render
方法为内容数组中的每个项目返回一个ContentItem
元素.
The application is made of two components: a top-level component called Application
, which manages (in its state) an array called content
, and a component called ContentItem
, which represents the UI and behavior of a single item from that array. Application
's render
method returns a ContentItem
element for each item in the content array.
要注意的一件事是,用于管理content
数组中的值的所有逻辑都是在Application
组件中处理的; ContentItem
组件将通过引用传递给Application
的removeItem
方法,单击时ContentItem
会将其委托给该方法.这将所有用于处理状态的逻辑保留在顶级组件内.
One thing to notice is that all of the logic for managing the values inside the content
array are handled in the Application
component; the ContentItem
components are passed a reference to Application
's removeItem
method, which the ContentItem
delegates to when clicked. This keeps all the logic for manipulating state inside the top-level component.