读书笔记:Tutorial of React.JS

来源:互联网 发布:哪个网络播放器最好用 编辑:程序博客网 时间:2024/06/06 05:46

原文链接

React is all about modular, composable components.

-

In React, components should always represent the state of the view and not only at the point of initialization.

-

该例子的Component结构

  • CommentBox
    • CommentList
      • Comment
    • CommentForm

Component的使用

var CommentBox = React.createClass({    render: function(){        return(<div className="commentBox"></div>        );    }});ReactDOM.render(  <CommentBox />,  document.getElementById('content'));

React.createClass()定义component构造器,该方法接受一个JSON 对象,其中键值render定义了关键函数–该函数返回该component对应的HTML结构.

值得注意的是,该HTML结构的语法是一块语法糖,由JSX实现.其相当于

// tutorial1-raw.jsvar 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'));

ReactDom.render
接受两个参数, 一个是markup格式的component变量声明,一个是目标位置, render函数生成的DOM将插入到该目标位置下.

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.

Note that native HTML element names start with a lowercase letter, while custom React class names begin with an uppercase letter.
(注意命名规则)

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

Component 中可以插入自定义componet

在元素之间传递数据

Let’s create the Comment component, which will depend on data passed in from its parent. Data passed in from a parent component is available as a ‘property’ on the child component. These ‘properties’ are accessed through this.props.
【从父级元素传来的数据,作为自己元素的一个属性使用】
Using props, we will be able to read the data passed to the Comment from the CommentList, and render some markup:

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

We access named attributes passed to the component as keys on this.props and any nested elements as this.props.children
变量要用花括号括住

(Q:What does it mean by “nested elements”?).

// tutorial5.jsvar 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>    );  }});

通过在标签中添加属性传递参数(如该例子中的<Comment>中的author属性。

使用markdown语法

使用前:这里写图片描述

...     {this.props.author}        </h2>        {this.props.children}  // <===      </div>      ...

修改为:

{md.render(this.props.children.toString())}

We need to convertthis.props.children from React’s wrapped text to a raw string that remarkable will understand so we explicitly call toString().
【但这个写法是不work的。】That’s React protecting you from an XSS attack. There’s a way to get around it (but the framework warns you not to use it):
This is a special API that intentionally makes it difficult to insert raw HTML, but for remarkable we’ll take advantage of this backdoor.
需要改写为:

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>    );  }});

md.render方法生成的是HTML格式的文本;要使其真正能实现HTML的功能,需要按

 { __html: nameOfYourHTMLContent }

的语法书写。放在一个对象中,key值为__html

传递来自Data Model的数据

此处以内置JSON文件作为简单示例

// CommentList的修改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">      //this.props.data.map方法的返回结果是一串**有效的HTML文本**,直接在`render`对应的方法的返回值中作为**变量**使用即可。        {commentNodes}  // <===Like this!      </div>    );  }});

this.props.data.map方法接受一个定义了componet及其数据的绑定格式的函数。该函数的参数就是数据的item

// CommenBox的修改var CommentBox = React.createClass({  render: function() {    return (      <div className="commentBox">        <h1>Comments</h1>        // 数据首先到达父级元素CommentBox,        // 在子元素CommentList中作为属性使用        <CommentList data={this.props.data} />         <CommentForm />      </div>    );  }});ReactDOM.render(  <CommentBox data={data} />,  document.getElementById('content'));

Componet 的属性data中绑定数据源:在ReactDOM.render中修改 data={nameOfDataSourceVariable}

绑定服务器上的数据源

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

使用Componenturl属性。结合server端的代码使用。

app.get('/api/comments', function(req, res) {  fs.readFile(COMMENTS_FILE, function(err, data) {    if (err) {      console.error(err);      process.exit(1);    }    res.json(JSON.parse(data));  });});...var COMMENTS_FILE = path.join(__dirname, 'comments.json');

数据的请求是最后在ReactDOM.render函数中进行的,任何component的声明都只提供了一个空框架/模型

使用Component的state属性实现交互

To implement 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.

// tutorial12.jsvar CommentBox = React.createClass({  getInitialState: function() {    return {data: []};  },  render: function() {    return (      <div className="commentBox">        <h1>Comments</h1>        <CommentList 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. When the server fetches data, we will be changing the comment data we have.

getInitialState和render一样,都是固定语法

实现数据的实时更新

We’re going to use jQuery to make an asynchronous request to the server we started earlier to fetch the data we need

再次修改CommentBox,添加componentDidMount函数:(注意componentDidMount也是固定语法)

// tutorial13.jsvar CommentBox = React.createClass({  getInitialState: function() {    return {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>Comments</h1>        <CommentList data={this.state.data} />        <CommentForm />      </div>    );  }});

componentDidMount
核心代码是this.setState()。它做了两件事:1.从服务器取得最新数据并覆盖旧的;2.让UI自动更新。
传参语法:{data: nameOfCallBackParams}

优化代码,将AJAX请求数据的部分分离出来:

...  componentDidMount: function() {  // AJAX is wrapped up in a func.    this.loadCommentsFromServer();     /* call AJAX when the component is first loaded and every 2 seconds after that. */    setInterval(this.loadCommentsFromServer, this.props.pollInterval);  },...ReactDOM.render(  <CommentBox url="/api/comments" pollInterval={2000} />,  document.getElementById('content'));

设置请求间隔:在render函数中使用pollInterval属性设置:pollInterval={numberOfMiliseconds}

实现用户数据输入(表单提交)

区别于传统DOM的处理方式

【传统DOM带来的问题】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. This is not ideal as the state of the view will differ from that of the component.
we will be usingthis.stateto 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 <input> elements with a value set are called controlled components.

修改CommentForm

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});  },  ...<input    type="text"          placeholder="Say something..."          value={this.state.text}          onChange={this.handleTextChange}        />

注意setState语法: this.setState({nameOfProperty: nameOfCallbackParam.target.value});
onChange是一个event.

实现表单提交

Let’s make the form interactive. When the user submits the form, we should clear it,
submit a request to the server,
and refresh the list of comments.
To start, let’s listen for the form’s submit event and clear it.

修改CommentForm:

  handleSubmit: function(e) {    e.preventDefault();    var author = this.state.author.trim();    var text = this.state.text.trim();    /*.trim()?*/    if (!text || !author) {      return;    }    // TODO: send request to the server    /*clear the form after submission*/    this.setState({author: '', text: ''});  },  ...  render: function() {    return (      <form className="commentForm" onSubmit={this.handleSubmit}>      ...

formonSubmit监听了提交事件。

Call preventDefault() on the event to prevent the browser’s default action of submitting the form.

实现列表刷新:子元素传数据到父元素

It makes sense to do all of this logic in CommentBox since CommentBox owns the state that represents the list of comments.
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, binding it to the child’s onCommentSubmit event. Whenever the event is triggered, the callback will be invoked.
【用回调函数的方式,实现从子元素传递数据到父元素】

修改CommentBox的render:

  handleCommentSubmit: function(comment) {    // TODO: submit to the server and refresh the list  },        /* LOOK AT HERE ↓ */    this.props.onCommentSubmit({author: author, text: text});    this.setState({author: '', text: ''});  },render: ...function() {    return (      <div className="commentBox">        <h1>Comments</h1>        <CommentList data={this.state.data} />        /* LOOK AT HERE ↓ */        <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:

修改CommentForm:

... handleSubmit: function(e) {    e.preventDefault();    var author = this.state.author.trim();    var text = this.state.text.trim();    if (!text || !author) {      return;    }    /* 父级传来的回调函数通过    this.props.nameOfCallBackMethod来调用!LOOK AT HERE ↓ */    this.props.onCommentSubmit({author: author, text: text});    this.setState({author: '', text: ''});  },...

得到CommentForm的数据之后,CommentBox就可以将数据提交到服务器(url)更新列表了:

// CommentBox handleCommentSubmit: function(comment) {    $.ajax({      url: this.props.url,      dataType: 'json',      type: 'POST',      //  发送数据的ajax请求      data: comment,       success: function(data) {        this.setState({data: data});      }...);  },

将回调结果’comment’写入键值’data’。

优化列表刷新:本地刷新

In  CommentBox handleCommentSubmit: function(comment) {    var comments = this.state.data;    // comments是当前的数据大数组    comment.id = Date.now();    // 将新的comment添加(concat)到comments中    var newComments = comments.concat([comment]);    // 本地手动更新    this.setState({data: newComments});    $.ajax({    ...

总结

  • 学会了component的基本使用;
  • 数据和component的绑定;
  • 数据源的绑定(本地/服务器)。
  • JSX作为语法糖的使用。
  • 运用元素的prop(properties)进行数据传递,以及在此之上利用回调函数进行从子元素到父元素数据传递
  • 使用markdown渲染文本。

Q:.state和.props在数据传递上的区别是什么?

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】是不会变动的,在生成时初始化并不再更改。
this.state is private to the component and can be changed by calling this.setState(). When the state updates, the component re-renders itself.

动手

最近刚好在学SQL,可以用react.js做一个数据可视化的工具。

拓展

Forms Article:进阶使用Controlled Components
automatically binds :事件绑定
thinking in react

0 0
原创粉丝点击