1.My first component
We will have the following component structure:
-CommentBox
-CommentList
-Comment
-CommentForm
//index.html
//tutorial1.js
var CommentBox = React.createClass({
render:function(){
return (
Hello , World !
);
}
});
ReactDOM.render(
,
document.getElementById('content')
);
//JSX syntax
// tutorial1-raw.js
var CommentBox = React.createClass({displayName: 'CommentBox',
render: function() {
return (
React.createElement('div', {className: "commentBox"},
"Hello, world! I am a CommentBox."
)
);
}
});
ReactDOM.render(
React.createElement(CommentBox, null),
document.getElementById('content')
);
(1)Note that native Notice how we're mixing (1)Data passed in from a parent component is available as a 'property' (属性)on the child component Note that we have passed some data from the parent This is a special API that intentionally makes it difficult to insert raw HTML, but for remarkable we'll take advantage of this backdoor. So far we've been inserting the comments directly in the source code. Instead, let's render a blob of JSON data into the comment list. Eventually this will come from the server, but for now, write it in your source: We need to get this data into Now that the data is available in the CommentList, let's render the comments dynamically: Let's replace the hard-coded data with some dynamic data from the server. We will remove the data prop and replace it with a URL to fetch: This component is different from the prior components because it will have to re-render itself.The component won't have any data until the request from the server comes back, at which point the component may need to render some new comments. So far, based on its props, each component has rendered itself once. To implement (执行)interactions(互动), we introduce mutable(可变的) state to the component. render() methods are written declaratively as functions of this.props and this.state. The framework guarantees the UI is always consistent with the inputs.(框架保证UI总是与输入保持一致) When the server fetches data(服务器获取数据), we will be changing the comment data we have. Let's add an array of comment data to the When the component is first created, we want to GET some JSON from the server and update the state to reflect the latest data. We're going to use jQuery to make an asynchronous request (异步请求)to the server we started earlier(之前启动的服务器) to fetch the data we need. The data is already included in the server you started (based on the Here, All we have done is move the AJAX call to a separate method and call it when the component is first loaded and every 2 seconds after that.(我们所做的是把 Now it's time to build the form. Our Controlled components(受控组件) With the traditional In Hence, we will be using Events Submitting the form To start, let's listen for the form's submit event and clear it. Call Callbacks as props We need to pass data from the child component back up to its parent. Now that Now that the callbacks are in place, all we have to do is submit to the server and refresh the list: Our application is now feature complete but it feels slow to have to wait for the request to complete before your comment appears in the list. (功能齐全,但是评论出现在列表之前需要等待请求完成,所以感觉很慢) Congrats!HTML
element names start with a lowercase letter , while customReact
class names begin with an uppercase letter.
译:HTML 标签
以小写字母开头,而 React 组件
以大写字母开头。
(2)We pass some methods in a JavaScript object to React.creatClass()
to create a new React component . The most important of these methods is called render
which returns a tree of React components that will eventually render to HTML.
译:我们在JS 对象中传递一些方法到React.createClass()
来创建一个新的React 组件
。其中最重要的方法是 render
,他返回一个 React 组件树,最终将渲染到HTML
。
(3)TheReact
div components.You can think of these as markers or pieces of data that React
knows how to handle.
(4)You do not have to return basic HTML
. You can return a tree of components that you (or someone else) built. This is what makes React
composable(可组合): a key tenet of maintainable frontends.(可维护前端的宗旨)
(5)ReactDOM.render()
instantiates(实例化) the root component, starts the framework(框架), and injects( 注入、添加 ) the markup into a raw DOM element
, provided as the second argument(参数).
(6)It is important that ReactDOM.render
remain at the bottom of the script for this tutorial. ReactDOM.render
should only be called after the composite components(复合组件) have been defined.
2.Composing components
var CommentBox = React.createClass({
render: function() {
return (
Comments
HTML tags
and components
we've built. HTML components
are regular React components
, just like the ones you define, with one difference. The JSX compiler
will automatically rewrite HTML tags
to React.createElement(tagName)
expressions and leave everything else alone. ( JSX 编译器
会自动将HTML标签
重写为React.createElement(tagname)
表达式,并留下其余的一切 )This is to prevent the pollution of the global namespace.(防止全局命名空间的污染)3.Using props
var Comment = React.createClass({
render: function() {
return (
{this.props.author}
{this.props.children}
These 'properties' are accessed through this.props.
(2)By surrounding a JavaScript
expression in braces(大括号) inside JSX
(as either an attribute or child), you can drop text
or React components
into the tree. We access named attributes passed to the componentas keys on this.props
and any nested elements as this.props.children
.(我们通过this.props
上的键 或 任何像this.props.children
的嵌套元素 访问传递给组件的命名属性)4.Component Properties(组件属性)
// tutorial5.js
var CommentList = React.createClass({
render: function() {
return (
CommentList
component to the child Comment
components.5.Adding Markdown
// tutorial7.js
var Comment = React.createClass({
rawMarkup: function() {
var md = new Remarkable();
var rawMarkup = md.render(this.props.children.toString());
return { __html: rawMarkup };
},
render: function() {
return (
{this.props.author}
Remember: by using this feature you're relying on remarkable to be secure. In this case, remarkable automatically strips HTML markup and insecure links from the output.6.Hook up the data model(挂钩数据模型)
// tutorial8.js
var data = [
{id: 1, author: "Pete Hunt", text: "This is one comment"},
{id: 2, author: "Jordan Walke", text: "This is *another* comment"}
];
CommentList
in a modular way(模块化的方式). Modify(修改) CommentBox
and theReactDOM.render()
call to pass this data into theCommentList
via(通过) props:// tutorial9.js
var CommentBox = React.createClass({
render: function() {
return (
Comments
var CommentList = React.createClass({
render:function(){
var commentNodes = this.props.data.map(function(comment){
return(
7.Fetching from the server
// tutorial11.js
ReactDOM.render(
Note: the code will not be working at this step.8.Reactive state
props
are immutable(不可变): they are passed from the parent and are "owned" by the parent(props 从parent而来,归 parent 所有)this.state
is private to the component and can be changed by calling this.setState()
. When the state updates, the component re-renders itself.CommentBox
component as its state:var CommentBox = React.createClass({
getInitialState:function(){ //getInitialState : function(){ }
return { data: [] };
},
render:function(){
return(
Comments
getInitialState()
executes exactly once during the lifecycle of the component and sets up the initial state of the component.(getInitialState()
在组件的生命周期中执行一次,并且设置组件的初始状态)9.Updating state
comments.json
file), so once it's fetched, this.state.data
will look something like this://comments.json
[
{"id" : "1" , "author" : "Pete Hunt" , "text" : "This is one comment" },
{"id" : "2" , "author" : "Angela Ma" , "text" : "This is my comment" }
]
var CommentBox = React.createClass({
getInitialState:function(){
return{[data:data]};
},
componentDidMount:function(){
$.ajax({
url : this.props.url,
dataType :'json',
cache:false,
success:function(data){
this.setState({data:data});
}.bind(this),
error:function(xhr,status,err){
console.error(this.props.url,status,err.toString());
}.bind(this)
});
},
render:function(){
return(
Comment
componentDidMount
is a method called automatically by React after a component is rendered for the first time. (componentDidMount
是组件第一次渲染后,由React自动调用的方法)
The key to dynamic updates is the call to this.setState()
.(动态更新的关键是 this.setState()
的调用。) We replace the old array of comments with the new one from the server and the UI automatically updates itself.( 我们用服务器中的新数组替换旧数组的内容,UI自动更新自己。)
Because of this reactivity, it is only a minor change to add live updates.(由于这种反应性,添加实时更新只是很小的改变。)var CommentBox=React.createClass({
loadCommentsFromServer:function(){ //!!!!!
$.ajax({
url:this.props.url,
dataType:'json',
cache:false,
success:function(data){
this.setState({data:data});
}.bind(this),
error:function(xhr,status,err){
console.error(this.props.url,status,err.toString());
}.bind(this)
});
},
getInitialState:function(){
return {data:[]};
},
componentDidMount:function(){ // !!!!
this.loadCommentsFromServer();
setInterval(this.loadCommentsFromServer,this.props.pollInterval);
},
render:function(){
return(
Comments
AJAX
调用封装成单独的方法,并在组件第一次加载时调用它)10.Adding new comments
CommentForm
component should ask the user for their name and comment text and send a request to the server to save the comment.(向服务器发送请求来保存这些内容)// tutorial15.js
var CommentForm = React.createClass({
render: function() {
return (
);
}
});
DOM
, input
elements are rendered and the browser manages the state (its rendered value). As a result, the state of the actual DOM
will differ from that of the component.(在传统的DOM
中,input
元素被渲染,浏览器管理状态(渲染值),因此,实际的DOM
状态(输入值)与组件的状态(初始值)不同)。React
, components should always represent the state of the view and not only at the point of initialization.(在``React`中,组件始终表示视图的状态,而不仅仅是初始值)this.state
to save the user's input as it is entered. We define an initial state with two properties author
and text
and set them to be empty strings.
In our elements, we set the
value
prop to reflect the state
of the component and attach onChange
handlers to them.
These elements with a
value
set are called controlled components.(具有value
值的Input
元素,称为受控组件)// tutorial16.js
var CommentForm = React.createClass({
getInitialState: function() {
return {author: '', text: ''};
},
handleAuthorChange: function(e) {
this.setState({author: e.target.value});
},
handleTextChange: function(e) {
this.setState({text: e.target.value});
},
render: function() {
return (
);
}
});
React attaches event handlers to components using a camelCase naming convention.(驼峰命名法)
React.createClass(...)
automatically binds each method to its component instance, obviating the need for explicit binding.(React.createClass(...)
自动绑定每个方法到它的组件实例,避免了显式绑定的需要 。)// tutorial17.js
var CommentForm = React.createClass({
getInitialState: function() {
return {author: '', text: ''};
},
handleAuthorChange: function(e) {
this.setState({author: e.target.value});
},
handleTextChange: function(e) {
this.setState({text: e.target.value});
},
handleSubmit: function(e) { //!!!
e.preventDefault();
var author = this.state.author.trim();
var text = this.state.text.trim();
if (!text || !author) {
return;
}
// TODO: send request to the server
this.setState({author: '', text: ''});
},
render: function() {
return (
);
}
});
preventDefault()
on the event to prevent the browser's default action of submitting the form.(阻止浏览器提交表单的默认行为)
When a user submits a comment, we will need to refresh the list of comments to include the new one. It makes sense to do all of this logic in CommentBox
since CommentBox
owns the state that represents the list of comments.(在CommentBox
中执行这个逻辑是有意义的,因为CommentBox
持有评论列表的状态)
We do this in our parent's render method by passing a new callback (handleCommentSubmit)
into the child(我们通过一个新的回调handleCommentSubmit
传递给子组件), binding(绑定) it to the child's onCommentSubmit
event.
Whenever the event is triggered, the callback will be invoked:(当事件被触发,回调将被调用)// tutorial18.js
var CommentBox = React.createClass({
loadCommentsFromServer: function() {
$.ajax({
url: this.props.url,
dataType: 'json',
cache: false,
success: function(data) {
this.setState({data: data});
}.bind(this),
error: function(xhr, status, err) {
console.error(this.props.url, status, err.toString());
}.bind(this)
});
},
handleCommentSubmit: function(comment) { //!!!
// TODO: submit to the server and refresh the list
},
getInitialState: function() {
return {data: []};
},
componentDidMount: function() {
this.loadCommentsFromServer();
setInterval(this.loadCommentsFromServer, this.props.pollInterval);
},
render: function() {
return (
Comments
CommentBox
has made the callback available to CommentForm
via the onCommentSubmit
prop, the CommentForm
can call the callback when the user submits the form:// tutorial19.js
var CommentForm = React.createClass({
getInitialState: function() {
return {author: '', text: ''};
},
handleAuthorChange: function(e) {
this.setState({author: e.target.value});
},
handleTextChange: function(e) {
this.setState({text: e.target.value});
},
handleSubmit: function(e) {
e.preventDefault();
var author = this.state.author.trim();
var text = this.state.text.trim();
if (!text || !author) {
return;
}
this.props.onCommentSubmit({author: author, text: text}); //!!!
this.setState({author: '', text: ''});
},
render: function() {
return (
);
}
});
var CommentBox = React.createClass({
loadCommentsFromServer: function() {
$.ajax({
url: this.props.url,
dataType: 'json',
cache: false,
success: function(data) {
this.setState({data: data});
}.bind(this),
error: function(xhr, status, err) {
console.error(this.props.url, status, err.toString());
}.bind(this)
});
},
handleCommentSubmit: function(comment) { //!!!
$.ajax({
url: this.props.url,
dataType: 'json',
type: 'POST',
data: comment,
success: function(data) {
this.setState({data: data});
}.bind(this),
error: function(xhr, status, err) {
console.error(this.props.url, status, err.toString());
}.bind(this)
});
},
getInitialState: function() {
return {data: []};
},
componentDidMount: function() {
this.loadCommentsFromServer();
setInterval(this.loadCommentsFromServer, this.props.pollInterval);
},
render: function() {
return (
Comments
11.Optimization: optimistic updates(优化)
We can optimistically add this comment to the list to make the app feel faster.(我们可以乐观地添加这个评论到列表,使应用程序感觉更快。)// tutorial21.js
var CommentBox = React.createClass({
loadCommentsFromServer: function() {
$.ajax({
url: this.props.url,
dataType: 'json',
cache: false,
success: function(data) {
this.setState({data: data});
}.bind(this),
error: function(xhr, status, err) {
console.error(this.props.url, status, err.toString());
}.bind(this)
});
},
handleCommentSubmit: function(comment) {
var comments = this.state.data; //!!!
// Optimistically set an id on the new comment. It will be replaced by an
// id generated by the server. In a production application you would likely
// not use Date.now() for this and would have a more robust system in place.
comment.id = Date.now(); //!给新的comment设置id,它将被服务器生成的id-Date.now()替换,希望之后不要使用Date.now()
var newComments = comments.concat([comment]); //!!!
this.setState({data: newComments}); //!!!
$.ajax({
url: this.props.url,
dataType: 'json',
type: 'POST',
data: comment,
success: function(data) {
this.setState({data: data});
}.bind(this),
error: function(xhr, status, err) {
this.setState({data: comments}); //!!!
console.error(this.props.url, status, err.toString());
}.bind(this)
});
},
getInitialState: function() {
return {data: []};
},
componentDidMount: function() {
this.loadCommentsFromServer();
setInterval(this.loadCommentsFromServer, this.props.pollInterval);
},
render: function() {
return (
Comments
We have just built a comment box in a few simple steps.