There are a few basic tenets to keep in mind that may help you build a good React application:
Your UI should be a function of the data个
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.
在Reaction中,每个组件(理想情况下)都是两条数据的函数-传递给组件实例的properties条数据和组件内部管理的state条数据.给定相同的属性(或"props ")和状态,组件应该以相同的方式呈现.
This can be a bit of an abstract idea without concrete examples, so keep it in mind as we move on for now.
Don't touch the 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.
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).
Not manipulating the DOM goes hand-in-hand with "UI as a function of the data," above.
Invert the data flow
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.
Create composable components
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.
Beware mutable data个
由于组件状态只能通过从组件内部调用this.setState
来更新,因此对可变数据保持警惕是有帮助的.当有多个功能(或组件!)时,情况更是如此可能会在同一时间更新可变对象;React可能会try 批量更改状态,您可能会丢失更新!正如Eliseu Monar的 comments 中提到的,在变异之前先考虑克隆可变对象.React有immutability helpers个可以提供帮助.
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.
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.
通过应用上述原则,您的最终应用程序可能如下所示:
/** @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);
You can see the code in action in this JSFiddle: http://jsfiddle.net/BinaryMuse/D59yP/
该应用程序由两个组件组成:一个名为Application
的顶级组件(在其状态下)管理一个名为content
的数组;另一个名为ContentItem
的组件,表示该数组中单个项的UI和行为.Application
的render
方法为内容数组中的每个项目返回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.