React-Tutorial學(xué)習(xí)筆記

1.My first component


We will have the following component structure:

-CommentBox
  -CommentList
    -Comment
  -CommentForm
//index.html
<div id="content"></div>
//tutorial1.js
var CommentBox = React.createClass({
  render:function(){
    return (
      <div className="commentBox">
        Hello , World ! 
      </div>
    );
  }
});
ReactDOM.render(
  <CommentBox />,
  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 HTMLelement names start with a lowercase letter , while customReactclass names begin with an uppercase letter.
譯:HTML 標(biāo)簽以小寫字母開頭,而 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()來創(chuàng)建一個新的React 組件。其中最重要的方法是 render ,他返回一個 React 組件樹,最終將渲染到HTML
(3)The<div> tags are not actual DOM nodes; they are instantiations of React 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.(可維護(hù)前端的宗旨)
(5)ReactDOM.render() instantiates(實例化) the root component, starts the framework(框架), and injects( 注入、添加 ) the markup into a raw DOM element, provided as the second argument(參數(shù)).
(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(復(fù)合組件) have been defined.

2.Composing components


var CommentBox = React.createClass({
  render: function() {
    return (
      <div className="commentBox">
        <h1>Comments</h1>
        <CommentList />
        <CommentForm />
      </div>
    );
  }
});

Notice how we're mixing HTML tags and components we've built. HTML componentsare 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標(biāo)簽重寫為React.createElement(tagname)表達(dá)式,并留下其余的一切 )This is to prevent the pollution of the global namespace.(防止全局命名空間的污染)

3.Using props


var Comment = React.createClass({
  render: function() {
    return (
      <div className="comment">
        <h2 className="commentAuthor">
          {this.props.author}
        </h2>
        {this.props.children}
      </div>
    );
  }
});

(1)Data passed in from a parent component is available as a 'property' (屬性)on the child component
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 (
      <div className="commentList">
        <Comment author="Pete Hunt">This is one comment</Comment>
        <Comment author="Jordan Walke">This is *another* comment</Comment>
      </div>
    );
  }
});

Note that we have passed some data from the parent CommentListcomponent to the child Commentcomponents.

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 (
      <div className="comment">
        <h2 className="commentAuthor">
          {this.props.author}
        </h2>
        <span dangerouslySetInnerHTML={this.rawMarkup()} />
      </div>
    );
  }
});

This is a special API that intentionally makes it difficult to insert raw HTML, but for remarkable we'll take advantage of this backdoor.
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(掛鉤數(shù)據(jù)模型)


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:

// tutorial8.js
var data = [
  {id: 1, author: "Pete Hunt", text: "This is one comment"},
  {id: 2, author: "Jordan Walke", text: "This is *another* comment"}
];

We need to get this data into 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 (
      <div className="commentBox">
        <h1>Comments</h1>
        <CommentList data={this.props.data} /> //Modify CommentBox to get this data into CommentList
        <CommentForm />
      </div>
    );
  }
});

ReactDOM.render(
  <CommentBox data={data} />,      //Modify ReactDOM.render()
  document.getElementById('content')
);

Now that the data is available in the CommentList, let's render the comments dynamically:

var CommentList = React.createClass({
  render:function(){
    var commentNodes = this.props.data.map(function(comment){
      return(
        <Comment author={comment.author} ,key = {comment.id}>
          {comment.text}
        </Comment>
      );
    });
    return(
      <div className="commentList">
         {commentNodes}
      </div>
    );
  }
});

7.Fetching from the server


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:

// tutorial11.js
ReactDOM.render(
  <CommentBox url="/api/comments" />,
  document.getElementById('content')
);

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.
Note: the code will not be working at this step.

8.Reactive state


So far, based on its props, each component has rendered itself once.props are immutable(不可變): they are passed from the parent and are "owned" by the parent(props 從parent而來,歸 parent 所有)

To implement (執(zhí)行)interactions(互動), we introduce mutable(可變的) state to the component. this.state is private to the component and can be changed by calling this.setState(). When the state updates, the component re-renders itself.

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(服務(wù)器獲取數(shù)據(jù)), we will be changing the comment data we have. Let's add an array of comment data to theCommentBoxcomponent as its state:

var CommentBox = React.createClass({
  getInitialState:function(){     //getInitialState : function(){ } 
    return { data: [] };
  },
  render:function(){
    return(
      <div className="commentBox">
         <h1>Comments</h1>
         <CommentList data={this.state.data} />   //data={this.state.data}
         <CommentForm />
      </div>
    );
  }
});

getInitialState() executes exactly once during the lifecycle of the component and sets up the initial state of the component.(getInitialState()在組件的生命周期中執(zhí)行一次,并且設(shè)置組件的初始狀態(tài))

9.Updating state


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(之前啟動的服務(wù)器) to fetch the data we need. The data is already included in the server you started (based on the 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(
      <div  className="commentBox">
        <h1>Comment</h1>
        <CommentList  data={this.state.data} />
        <CommentForm />
      </div>
    );
  }
  
});

Here, componentDidMount is a method called automatically by React after a component is rendered for the first time. (componentDidMount是組件第一次渲染后,由React自動調(diào)用的方法)
The key to dynamic updates is the call to this.setState().(動態(tài)更新的關(guān)鍵是 this.setState()的調(diào)用。) We replace the old array of comments with the new one from the server and the UI automatically updates itself.( 我們用服務(wù)器中的新數(shù)組替換舊數(shù)組的內(nèi)容,UI自動更新自己。)
Because of this reactivity, it is only a minor change to add live updates.(由于這種反應(yīng)性,添加實時更新只是很小的改變。)

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(
              <div className="commentBox">
                <h1>Comments</h1>
                <CommentList data={this.state.data}/>
                <CommentForm />
              </div>
            );
          }
        });

ReactDOM.render(
  <CommentBox url="/api/comments" pollInterval={2000} />,     //!!!
  document.getElementById('content')
);

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.(我們所做的是把AJAX調(diào)用封裝成單獨的方法,并在組件第一次加載時調(diào)用它)

10.Adding new comments


Now it's time to build the form. Our CommentForm component should ask the user for their name and comment text and send a request to the server to save the comment.(向服務(wù)器發(fā)送請求來保存這些內(nèi)容)

// tutorial15.js
var CommentForm = React.createClass({
  render: function() {
    return (
      <form className="commentForm">
        <input type="text" placeholder="Your name" />
        <input type="text" placeholder="Say something..." />
        <input type="submit" value="Post" />
      </form>
    );
  }
});

Controlled components(受控組件)

With the traditional 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.(在傳統(tǒng)的DOM中,input元素被渲染,瀏覽器管理狀態(tài)(渲染值),因此,實際的DOM狀態(tài)(輸入值)與組件的狀態(tài)(初始值)不同)。

In React, components should always represent the state of the view and not only at the point of initialization.(在``React`中,組件始終表示視圖的狀態(tài),而不僅僅是初始值)

Hence, we will be using 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<input> elements, we set the value prop to reflect the state of the component and attach onChange handlers to them.
These <input> 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 (
      <form className="commentForm">
        <input
          type="text"
          placeholder="Your name"
          value={this.state.author}
          onChange={this.handleAuthorChange}
        />
        <input
          type="text"
          placeholder="Say something..."
          value={this.state.text}
          onChange={this.handleTextChange}
        />
        <input type="submit" value="Post" />
      </form>
    );
  }
});

Events
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(...)自動綁定每個方法到它的組件實例,避免了顯式綁定的需要 。)

Submitting the form

To start, let's listen for the form's submit event and clear it.

// 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 (
      <form className="commentForm" onSubmit={this.handleSubmit}>   //!!!
        <input
          type="text"
          placeholder="Your name"
          value={this.state.author}
          onChange={this.handleAuthorChange}
        />
        <input
          type="text"
          placeholder="Say something..."
          value={this.state.text}
          onChange={this.handleTextChange}
        />
        <input type="submit" value="Post" />
      </form>
    );
  }
});

Call preventDefault() on the event to prevent the browser's default action of submitting the form.(阻止瀏覽器提交表單的默認(rèn)行為)

Callbacks as props
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中執(zhí)行這個邏輯是有意義的,因為CommentBox持有評論列表的狀態(tài))

We need to pass data from the child component back up to its parent.
We do this in our parent's render method by passing a new callback (handleCommentSubmit) into the child(我們通過一個新的回調(diào)handleCommentSubmit傳遞給子組件), binding(綁定) it to the child's onCommentSubmit event.
Whenever the event is triggered, the callback will be invoked:(當(dāng)事件被觸發(fā),回調(diào)將被調(diào)用)

// 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 (
      <div className="commentBox">
        <h1>Comments</h1>
        <CommentList data={this.state.data} />
        <CommentForm onCommentSubmit={this.handleCommentSubmit} />    //!?。?      </div>
    );
  }
});

Now that 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 (
      <form className="commentForm" onSubmit={this.handleSubmit}>
        <input
          type="text"
          placeholder="Your name"
          value={this.state.author}
          onChange={this.handleAuthorChange}
        />
        <input
          type="text"
          placeholder="Say something..."
          value={this.state.text}
          onChange={this.handleTextChange}
        />
        <input type="submit" value="Post" />
      </form>
    );
  }
});

Now that the callbacks are in place, all we have to do is submit to the server and refresh the list:

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 (
      <div className="commentBox">
        <h1>Comments</h1>
        <CommentList data={this.state.data} />
        <CommentForm onCommentSubmit={this.handleCommentSubmit} />
      </div>
    );
  }
});

11.Optimization: optimistic updates(優(yōu)化)

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. (功能齊全,但是評論出現(xiàn)在列表之前需要等待請求完成,所以感覺很慢)
We can optimistically add this comment to the list to make the app feel faster.(我們可以樂觀地添加這個評論到列表,使應(yīng)用程序感覺更快。)

// 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設(shè)置id,它將被服務(wù)器生成的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 (
      <div className="commentBox">
        <h1>Comments</h1>
        <CommentList data={this.state.data} />
        <CommentForm onCommentSubmit={this.handleCommentSubmit} />
      </div>
    );
  }
});

Congrats!
We have just built a comment box in a few simple steps.

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容