这是一篇国外上了hacker news头条的文章,做者利用一个小案例的将jQuery以及React进行了对比,解释了React的优点,将这篇文章翻译过来,指望可以同你们一块儿进步~原文地址,下面是正文:javascript
我也据说React.js很是棒,最近也花了一些时间来研究它。如今我使用React起来感受很是舒服,我决定在这一方面写一个简单的教程。css
在开始以前,我想声明一下我写这篇文章的受众。html
“learn code the hard way”系列的做者Zed Shaw 最近写了一篇很是棒的博客叫作“Early v.s. Beginning Coders”,在这篇博客里,Zed批评了那些声称他们的教程是完彻底全适合初学者的编程培训人员,实际上,这些教程对于大多数初学者来讲是不适合的。 前端
我不想犯一样的错误。对于没有尝试过React的工程师里面,有些人喜欢使用像backbone、Ember或者Angular之类的JS框架,有些人对于JavaScript很是了解,有些人仅仅了解jQery。对一类工程师有效的教程未必对于另外一类也有用。java
在这个教程里面,我这篇文章的受众是我上面提到的第三种类型的工程师:了解jQuery的工程师,详细点:react
能够作简单的HTML/CSS/jQuery编写的设计者 知道怎样使用jQuery插件的开发者jquery
依赖Bootstrap以及简单的jQuery来实现简单的前端效果的后端开发者git
任何在编写JavaScript代码时习惯复制粘贴而不是本身编写的人angularjs
若是你已经习惯本身编写JavaScript代码或者使用其余的前端框架例如Backbone\Ember\Angular,这个教程并不适合你,个人代码风格也会让你疑惑。有不少其余很是好的教程值得你去学习,例如React官方教程github
一样的,若是你对React已经足够了解,那么你可能会认为这个教程很低级,由于我在其中大部分都是些的关于React的state的知识,没有涉及到组件以及其余的知识。
我认为这个教程的最适宜受众是那么习惯了使用jQuery的工程师,这门教程能够告诉他们React的优点在哪里~
好的,让咱们开始吧~
若是你学习能力很是好(而且复制粘贴代码而不是本身敲的话),这个教程应该会花费你大概一个小时。若是你用心研读而且本身敲代码的话,这个教程应该会花费你大概两个小时的时间。
若是你被某一处所困扰,能够作下面的:
在本页的底部发表你的评价
给我发email: shu@chibicode.com
在Twitter上联系我:@chibicode
在GIthub上面post你的疑问:Github连接
许多React教程从解释React的工做原理以及为何React是如此神奇开始,但个人教程不是!
相反,咱们会直接创建一个简单的UI项目,选择的工具是jQuery和React,利用这个项目,咱们来解释它们之间的不一样点,我相信看完这个教程以后你可以思考的更远而不只仅只是编写了一个UI项目。
咱们将要作的UI与推特上面的Tweet Box很像,固然,咱们对它的功能进行了精简,但愿你可以以为这个例子可以帮助你更好的了解React~
(第一步能够略过,各位能够在本身的本地编辑器按照教程的步骤编写就行)
首先介绍这个教程将要使用的一个在线支持HTML/CSS/JS代码的编辑器:JSBin,它能够在线支持jQuery以及React.js代码。你可能比较熟悉类似的工具:codepen或者JSFiddle,选择JSBin的缘由仅仅只是个人习惯。
左边是HTML语句,右边是即时生成的结果(建议在本身的编辑器上面直接编写,尝试过有些效果JSBin上面不能彻底显示)
创建一个JSBin的帐号
建议你建立一个JSBin的帐号,在菜单栏上面点击Login或者Register就能够。
在建立帐号以后,你就能够克隆JSBin的开源的代码到你的帐户,就如同Github同样,让咱们来尝试一下,点击JSBin菜单栏上面的“Save”按钮
若是你在JSBin的网站上面,你能够在菜单栏选择“Add Library”来引入流行的css或者JS框架
能够尝试作如下的事情:
点击“Add Library”来添加最新的Bootstrap
在button标签上面添加类名“btn btn-primary”
JSBin的示例:JSBin示例1
反馈回来的结果是这样的:
左侧的代码以下:
<!DOCTYPE html> <html> <head> <script src="https://code.jquery.com/jquery.min.js"></script> <link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css" rel="stylesheet" type="text/css" /> <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/js/bootstrap.min.js"></script> <meta charset="utf-8"> <title>JS Bin</title> </head> <body> <button class="btn btn-primary">Button</button> </body> </html>
下面来建立一个Tweet Box:
如今你应该已经习惯了JSBin的用法了吧?好的,让咱们来建立一个Tweet Box吧~仍然是在以前的代码中,改变<html>中<body>的内容:
<div class="well clearfix"> <textarea class="form-control"></textarea><br/> <button class="btn btn-primary pull-right">Tweet</button> </div>
咱们将会使用BootStrap中的类名,如:form-control
、well
、clearfix
等等,可是这些仅仅只是为了美观,跟本教程没有任何关系,下面是代码以及结果:
<!DOCTYPE html> <html> <head> <script src="https://code.jquery.com/jquery.min.js"></script> <link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css" rel="stylesheet" type="text/css" /> <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/js/bootstrap.min.js"></script> <meta charset="utf-8"> <title>JS Bin</title> </head> <body> <div class="well clearfix"> <textarea class="form-control"></textarea><br/> <button class="btn btn-primary pull-right">Tweet</button> </div> </body> </html>
如今,该用一些JS来实现交互了,咱们会知足如下的需求:
需求一:Tweet按钮初始状态应该是不能点击的。只有当输入框中有字符输入,按钮才可以点击
下面就是一个demo
为了知足这个需求,在以前的代码中须要添加一些jQuery代码:
// 初始化状态 $("button").prop("disabled", true); // 文本框的值发生变化时 $("textarea").on("input", function() { // 只要超过一个字符,就 if ($(this).val().length > 0) { // 按钮能够点击 $("button").prop("disabled", false); } else { //不然,按钮不能点击 $("button").prop("disabled", true); } });
代码解释:
我使用的是标签名选择器:button
还有textarea
,在这个例子中没有必要给他们添加class类名或者id
为了实现按钮不能点击,使用代码$(...).prop(disabled, ...)
为了监听textarea
的变化,须要使用input
事件,这个标准浏览器上面都能获得支持
尝试在文本框中输入一些字符,注意观察button状态的改变
若是你感到困惑,建议多研究一下jQuery代码的使用方法,再来继续学习本教程。有不少学习jQuery很好的网站,例如Codecademy、Treehouse、Code school以及国内的慕课网。
第一个需求以及完成了,接下来咱们会利用React来实现一样的效果。
对于React,首先你须要了解的是你会在js中写标签,而不须要在HTML文档中。
下面来展现一下我说的是什么意思。下面是利用React.js所呈现的一样Tweet Box的代码
建议:你不须要明白代码的含义,仅仅尝试读一下代码就能够了
var TweetBox = React.createClass({ render: function() { return ( <div className="well clearfix"> <textarea className="form-control"></textarea> <br/> <button className="btn btn-primary pull-right">Tweet</button> </div> ); } }); React.render( <TweetBox />, document.body );
一些注意事项:
return (...)
不是JavaScript代码,而是一个HTML代码。在React中,你会使用一个特殊的语法叫作JSX,它可让你在JavaScript代码插入HTML代码。
其中的HTML代码仅仅只是与日常咱们所编写的HTML代码有一些相像,它们之间仍是有一些区别的。注意,它使用className
而不是class
,但它们很是像,因此你能够很快的学会它们。
浏览器不能解析JSX语法,因此当React运行你的JSX代码的时候,React会自动解析其中的HTML成JavaScript代码,从而使浏览器可以解析。
在主文档的HTML结构的<body></body>
标签中不须要写入HTML标签,咱们会在JavaScript中写入标签
常问的问题以及解答:
问题:React.createClass
还有React.render
有什么做用?我须要如今就明白它们吗?
回答:如今你不须要对这个感到担忧。React.createClass
建立一个有名字的UI组件(在这个例子中,就是TweetBox
)。而后经过React.render(< TweetBox />,document.body)
插入body这一个DOM节点中,如今你了解这些就足够了。
问题:在本地,我须要作一些特殊的事情去写JSX吗?
回答:是的,可是你只须要引入一个叫作JSX Transformer这个文件就好了。而在JSBin中,你只是须要增长一个React库,下面对这个会有介绍。
问题:在同一个区域将js还有HTML混在一块儿写,不是说是一种坏的风格吗?
回答:对于简单的页面而言,这样写可能会是一种坏的风格,可是对于大型的网页应用就不必定了。在大型的网页应用中,会有成百上千的UI,每个UI都包含他本身的标签以及行为。对于每个UI而言,若是标签还有事件行为放在一块儿,会更加利于维护。React就是设计用来制做大型的网页应用。众所周知的,React是全球最大的网页应用公司FaceBook开源而且使用的。
下面我会带着你一步一步的来写上面展现的React的代码。
首先,制做了一个简单的HTML页面,我在其中引入了React以及Bootstrap,代码以下:
JSBin示例1
<!DOCTYPE html> <html> <head> <script src="//fb.me/react-0.13.1.js"></script> <link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css" rel="stylesheet" type="text/css" /> <meta charset="utf-8"> <title>JS Bin</title> </head> <body> </body> </html>
在JSBin上面输入上面的代码以后,打开JavaScript标签选择“JSX(React)”
本地操做方法:在本地文档上能够在安装React以后,引入JSXTransformer文件*
<script src="../build/react.js"></script> <script src="../build/JSXTransformer.js"></script> <script type="text/jsx"></script>
上面是本地使用React的示例,具体的你们能够参考网上的其余教程,这里就不过多赘述了
如今你能够来写一些React代码了。能够尝试跟我一块儿敲下面的代码:
var TweetBox = React.createClass({ render: function() { } });
上面的代码是建立以UI组件的主体,它就像是jQuery中的$(function(){...})
同样重要。
为了构建一个UI,咱们必须在render()
方法中输入一些代码。如今,咱们来用div
标签来作一些简单的事情
var TweetBox = React.createClass({ render: function() { return ( <div> Hello World! </div> ); } });
如上面所示,在return
后面加一对括号(...)
,而后在里面写上标签。
JSX的一些注意事项
对于JSX,你须要记住一件事情:render()
语句中,在return(...)
只能有一个返回的大的闭合标签
下面的示例不会起做用(没有返回标签):
return ( Hello World! );
下面的示例也不会起做用(有两个返回标签):
return ( <span> Hello </span> <span> World </span> );
将UI组建插入到DOM节点:
如今咱们想将上面的“hello world”UI组件插入到DOM中,咱们须要加上代码React.render()在咱们刚刚写的代码下面:
var TweetBox = React.createClass({ render: function() { return ( <div> Hello World! </div> ); } }); React.render( <TweetBox />, document.body );
React.render
接收两个参数,第一个是UI对象,就是<VariableName />
,第二个参数是DOM对象(在这个例子中指document.body
)。将两个参数结合到一块儿,表明着将TweetBox
这个UI组件插入到body
里面。
如今你应该看到Hello World!
出现了,恭喜,你已经成功的写出了第一个React组件!
下面,咱们来写咱们真正的TweetBox
组件
如今,咱们在TweetBox里面插入真正的HTML结构,而不是Hello World
。在以前render
代码的基础上面替换成下面的代码。
return ( <div className="well clearfix"> <textarea className="form-control"></textarea> <br/> <button className="btn btn-primary pull-right">Tweet</button> </div> );
这里有两件事情须要注意:
不要使用class
,使用className
,由于JSX代码最终要翻译成JS代码, 而class
在最新版的JS标准中属于保留字.
若是你只是用<br>
,而不是<br />
,这个标签不会起做用。记住必定要在自闭合标签中加上/
其余的应该跟以前的jQuery的示例相同。
如今你应该能够在JSBin中看到TweetBox了。若是什么都没有,也许你应该仔细的检查一下你的代码。
这就是步骤四,这是这一步的JSBin。(可能JSBin不能呈现出咱们想要的效果,我依旧建议本身在本地敲这些代码)。
首先,咱们利用jQuery来实现这样的需求。
需求一:按钮初始状态禁用,当输入框内有字符输入的时候,按钮能够进行点击。
下面是咱们写的完成这个需求的jQuery代码:
// Initially disable the button $("button").prop("disabled", true); // When the value of the text area changes... $("textarea").on("input", function() { // If there's at least one character... if ($(this).val().length > 0) { // Enable the button. $("button").prop("disabled", false); } else { // Else, disable the button. $("button").prop("disabled", true); } });
下面来看看咱们怎么用React来完成这样的需求:
接着前面的JSBin的代码示例开始:
首先,让咱们给按钮设置初始状态:
render: function() { return ( ... <button className="..." disabled>Tweet</button> ... ); }
按钮如今的状态应该是禁用的了。
请注意,咱们是这样利用jQuery来实现一样的功能。
$("button").prop("disabled", true);
如今,咱们要实现文本框内有字符,button按钮可用的需求。
处理状态改变的事件
首先,咱们须要等待文本的输入。利用jQuery能够这样写:
$("textarea").on("input", function() { ... }
而在React中,咱们须要写一个事件处理方法,就叫它:handleChange
吧
React.createClass({ handleChange: function(event) { }, render: function() { ... } });
接下来,当文本框发生改变的时候,咱们调用这个方法。为此,咱们要对textarea
标签作些修改:
<textarea className="form-control" onChange={this.handleChange}></textarea>
在jQuery中,咱们使用input
事件,而在React中,咱们使用onChange
。接下来,你会从React文档中接触到事件在React JSX中的不一样用法,因此不用太过担忧。
更重要的是,咱们在JSX文档的HTML语句中使用{...}
语法来处理JavaScript代码。在这个示例中,由于handleChange
在UI组件中是一个方法,因此咱们在handleChange
的前面添加了this
来调用它。
若是你已经习惯了编写jQuery代码,那么这看起来是一个很是很差的代码风格。再次申明,无须担忧这个问题。在大型应用中,因为标签以及事件都组合在一个,代码会更加易于阅读以及修改。
为了确保handleChange
方法被调用,咱们在方法中加入console.log
语句。
handleChange: function(event) { console.log(event.target.value); },
event
对象包含target
,便是textarea
,咱们使用.value
来输出textarea
的值。
在你的JSBin中,打开console
控制台来查看输出,而后,在文本框中随便输入一些字符。
你也能够在这里尝试JSBin console尝试。
这就是步骤五的全部啦。tips:验证成功,能够在JSBin中关闭console
,在下一步,咱们再也不须要它。
接下来,我会重点解释jQuery代码风格以及React代码风格的最大不一样。
在jQuery中,当某些事件发生的时候,你会常常性改变DOM(像咱们以前作的那样):
在React中,你并不直接改变DOM。不一样的是,当事件发生时,你改变的是“状态”,而这,是经过调用this.setState
来实现。
以后,当每次“状态”改变的时候,render
会被调用,在调用的过程当中,能够改变“状态”
这就是在事件发生时更新UI的过程。没错,它确实比较难懂,因此,接下来,我会经过代码来解释这一过程。
写一个事件处理方法
接着上一步的JSBin的代码。首先,咱们须要初始化“状态”这一对象。若是没有这一步,什么做用都没有。
首先,咱们须要写一个特别的方法叫作getInitialState
,它是React自带的方法,它会返回一个JS对象,即初始状态。
在这个JS对象中,咱们要作些什么呢?让咱们在其中建立一个key
叫作text
。
var TweetBox = React.createClass({ getInitialState: function() { return { text: "" }; }, handleChange: ... render: ... });
接下来,咱们会设置一个事件处理器将“状态”中的text
设置成文本框内的内容。咱们利用一个叫作setState
的内置函数将文本框里的内容传递给text
。
handleChange: function(event) { this.setState({ text: event.target.value }); },
如今,让咱们来利用一些简单的debug语句检查一下是否是正确设置了text。
只须要在靠近render
末尾处添加this.state.text
,而且使用{...}
语句来调用JSX中的js代码。
render: function() { return ( <div ...> ... <button ...>Tweet</button> <br/> {this.state.text} </div> ) }
能够尝试在文本框中输入一些文本,一样的内容应该出现的按钮下方。你也能够在JSBin上面尝试。
如今,你应该对以前的内容有了深一点的理解。
删除以前的debug代码
一旦你确认了“状态”被正确设置,删除以前添加的debug代码。
<br/> {this.state.text}
可用/禁用 按钮
到这里,咱们能够稍微暂停一下,观察按钮的状态随着文本框的内容有无而改变。
经过“状态”,咱们可使用如下逻辑:
若是this.state.text.length === 0
,按钮应该是被禁用的。在React中,添加disabled
属性,而且设置返回值this.state.text.length === 0
。由于它是js代码,咱们须要把它“{}”
起来。
<button className="btn btn-primary pull-right" disabled={this.state.text.length === 0}>Tweet</button>
若是你在原生的HTML中写disabled="true"
或者disabled="false"
。在原生的HTML中,你须要移除disabled
属性来启用按钮。可是React不是原生的HTML,它遵循的是下面的原则:
若是你在JSX中使用disabled={true}
,它会编译成<button ... disabled>
若是你在JSX中使用disabled={false}
,disabled
属性会从button
标签中移除。
这个一样适用于其它布尔类型的属性,例如checked
,暂时这还不是正式的官方文档的写法,可是它应该会很快被添加进去。
如今的代码示例:JSBin。
小结
在进入下一个步骤以前,请牢记React和jQuery的区别:
在jQuery中,每发生一个事件,就须要改变一次DOM
在React中,每发生一个事件,只会改变state
“状态”,经过render
来映射如今的状态
下一个功能就是文本中剩余字数的提示:
功能说明:
字符字数会这样显示140 - the number of characters entered.
咱们会首先利用jQuery实现这样的功能,以后再利用React。
接着咱们上一步的jQuery代码,React代码暂时放到一边。从如今开始,每个小章节,我都会给出新的代码。这意味,在你看完每一步以后,均可以本身好好阅读这些代码。
JSBin代码示范
首先,在HTML两种经过添加span
标签来增长字符字数,请看下面的示范:
<textarea ...></textarea><br> <span>140</span> <button ...>Tweet</button>
在JS代码中添加如下:
$("textarea").on("input", function() { $("span").text(140 - $(this).val().length); ... });
好了,尝试输入字符,你会发现提示字符数会发生改变。
查看完整的JSBin示例
怎么用React实现跟上面同样的功能呢?也许,你能够本身尝试一下。
tips:你能够在JSBin上面尝试本身动手编写。
在这一步中,你不须要操做HTML。建议你在JSBin中关闭它。
代码编写建议:
没有必要去改变getInitialState()
或者handleChange()
能够尝试在render()
中操做this.state.text.length
参考代码:
在render()
的<br/>
后面添加
<span>{140 - this.state.text.length}</span>
这是到如今为止的JSBin代码连接
感受很简单?不明白为何React比jQuery优秀这么多?好吧,下一步更加复杂了,而那才是React真正的瑰宝。
让咱们在这个UI组件上面添加一个"Add Photo"按钮。以后的事情就有些有趣了。
不过,咱们不会真的上传图片。下面的,才是咱们须要作的。
当你在推特上传图片的时候,它会自动帮你计算剩下的能够输入的字符。而你每次上传一次图片,它将会占用23个你本来能够输入的字符字数。
仿照推特,接下来咱们所要作的就是:
建立一个"Add Button"按钮
点击按钮的时候,能够切换它的状态。若是它的状态是"on",按钮会显示✓ Photo Added
若是按钮的状态是"on",那么能够输入的字符的个数会降低23个
一样,若是按钮的状态是"on",即便没有文本输入,那么"Tweet"按钮也是能够点击的
JSBin中的demo,尝试点击"Add Photo"按钮,而后观察可输入字数以及"Tweet"按钮的变化
接着以前写过的jQuery代码继续~
首咱们将会同时修改HTML以及js。在这以前,咱们使用了一个选择器$("button")
,可是若是有两个button
标签的话就不太好用了。
接下来,咱们要开始修改HTML以下:
... <button class="js-tweet-button btn btn-primary pull-right" disabled>Tweet</button> <button class="js-add-photo-button btn btn-default pull-right">Add Photo</button> ...
下面来详细解释一下改变了那些内容:
添加了叫作"Add Photo"的按钮
分别给按钮添加类名js-tweet-button
以及js-add-photo-button
,给它们的前缀增长了'js',是由于这些只会在js中使用,在css中不会使用。
接下来,从新写js文件以下:
$("textarea").on("input", function() { $("span").text(140 - $(this).val().length); if ($(this).val().length > 0) { $(".js-tweet-button").prop("disabled", false); } else { $(".js-tweet-button").prop("disabled", true); } });
来详细解析一下有哪些变化:
(重要)由于要给Tweet按钮增长disabled
属性,因此我在第一行移除了$("button").prop("disabled", true);
用$(".js-tweet-button")
替换了$("button")
,这样就能够跟.js-add-photo-button
区分开来
增长按钮的功能
咱们开始来增长新的功能:
点击Add Photo
按钮来改变其状态。若是按钮的状态是"on",那么就让其显示✓ Photo Added
为了达到这样的效果,让咱们来增长下面的代码:
$("textarea").on("input", function() { ... }); $(".js-add-photo-button").on("click", function() { if ($(this).hasClass("is-on")) { $(this) .removeClass("is-on") .text("Add Photo"); } else { $(this) .addClass("is-on") .text("✓ Photo Added"); } });
咱们使用类名is-on
来检测按钮的状态,你能够点击或者仔细校对代码来理解它的原理。
递减字符计数
接下里,让咱们来实现这样的需求:
若是"Add Photo"按钮的状态是"on",那么可输入字符数会减小23
为了完成这样的需求,编辑代码以下:
if ($(this).hasClass("is-on")) { $(this) .removeClass("is-on") .text("Add Photo"); $("span").text(140 - $("textarea").val().length); } else { $(this) .addClass("is-on") .text("✓ Photo Added"); $("span").text(140 - 23 - $("textarea").val().length); }
每次点击的时候,咱们都会改变span
中的文本内容。若是按钮的状态是"on",那么可输入字符数就从117(可输入字符总数140-图片所占的字符数23)开始计数。
你能够点击"Add Photo"来检查上面的代码是否起做用
处理可输入字符提示的功能
上面的还远没有结束。即便"Add Photo"按钮的状态是"on",你在文本框里面输入字符,提示的剩余可输入字符并不会同步发生改变。
为了解决这个问题,咱们须要修改一下有关textarea的代码以下:
$("textarea").on("input", function() { if ($(".js-add-photo-button").hasClass("is-on")) { $("span").text(140 - 23 - $(this).val().length); } else { $("span").text(140 - $(this).val().length); } if (...) { ... });
如今,你能够点击"Add Photo"以后,在文本框里面输入一些字符检查一下功能是否实现。
我知道这已经花费了好长一段时间了...
请坚持下去,如今jQuery的代码看起来已经有点让人迷糊了,不过不要紧,多看几遍你应该就能理解了。
最后一个功能的完善
最后一个须要完善的功能是:
若是"Add Photo"的状态是"on",即便没有文本输入,"Tweet"按钮也应该可使用。
为了完成这个需求,咱们须要修改"Add Photo"按钮的click事件:
$(".js-add-photo-button").on("click", function() { if ($(this).hasClass("is-on")) { ... if ($("textarea").val().length === 0) { $(".js-tweet-button").prop("disabled", true); } } else { ... $(".js-tweet-button").prop("disabled", false); } });
代码解析:
若是"Add Photo"按钮的状态从"on"改变成"off"(if
语句),咱们须要检查是否有字符输入,若是没有,禁用按钮
若是"Add Photo"按钮的状态从"off"改变成"on"(else
语句),咱们须要检查是否有字符输入,若是有,按钮启用
打断一下
咱们尚未结束,下面的几步会发现代码的一个bug,你能够本身先尝试解决一下:
点击"Add Photo"按钮,使其状态从"off"改变成"on"
输入一些字符
删除全部的字符
你会发现,由于"Add Photo"按钮的状态是"on",因此"Tweet"按钮应该仍然是能够启用的,可是现状并非这样的
这意味咱们关于文本框的代码少了某些逻辑,为了解决这个bug,咱们须要再加一个if
判断
$("textarea").on("input", function() { ... if ($(this).val().length > 0 || $(".js-add-photo-button").hasClass("is-on")) { ... } else { ... } });
咱们增长了一个判断去检查"Tweet"按钮是否应该禁用
你能够多试几回,此次应该没有问题了。
再一次查看你的jQuery代码,很让人困惑,不是吗?若是你一直这样写下去,你就不得不借助大量的注释来帮助你记住以前的代码作了哪些。更进一步,这些代码也不利于你以后重构你的项目。
那么问题来了:为何这么快利用jQuery所写的代码就变得这么“丑陋”?
这就不得不谈到咱们以前提及的jQuery的风格。
再看这幅图:
当只有一个事件并且处理对象只有一个DOM的时候,利用jquery写出的代码是很是简单的。然而,当多个事件多个处理对象的时候,利用jQuery写的代码就有点恶心了。
想象一下,若是有更多的事件,上面的图片中会出现更多的箭头,与此同时,代码变得更加很差管理了。
理论上,你能够减轻这些缺陷经过重构出复用的代码,可是你仍然不得不每次增长新功能的时候,绞尽脑汁处理好它们之间的关系。
如今,让咱们来看看在React中作一样的事情是多么简单!
接着你以前的代码开始
首先,在JSX中添加"Add Photo"按钮:
<button ...>Tweet</button> <button className="btn btn-default pull-right">Add Photo</button>
给按钮添加一个事件,使其能够改变按钮的显示内容从Add Photo
到✓ Photo Added
。温习一下React的代码风格:
咱们将会:
建立一个状态变量用来追踪"Add Photo"按钮的状态是"on"仍是"off"
使用render()
中的状态来决定按钮使显示Add Photo
仍是✓ Photo Added
在click事件发生时改变其状态
对于第一步,咱们会构造一个getInitialState
方法,在其中设置一对键值对来记录图片是否上传:
getInitialState: function() { return { text: "", photoAdded: false }; },
咱们会对JSX中的"Add Photo"button标签作一些改变。咱们能够经过下面的语句使其实现若是this.state.photoAdded
是true,那么button上面就会显示"Photo Added":
<button className="btn btn-default pull-right"> {this.state.photoAdded ? "✓ Photo Added" : "Add Photo" } </button>
对于第三步,咱们会在JSX中给文本框增长一个点击事件:
<button className="btn btn-default pull-right" onClick={this.togglePhoto}> {this.state.photoAdded ? "✓ Photo Added" : "Add Photo" } </button>
而且增长一个方法this.state.photoAdded
用来改变button按钮的状态:
togglePhoto: function(event) { this.setState({ photoAdded: !this.state.photoAdded }); },
如今,你能够尝试点击一下,你会发现"Add Photo"显示的内容会随着你的点击而改变。
可输入字符提示
咱们将会接着实现下面的功能:
若是"Add Photo"按钮的状态是"on",可输入字符串将会减小23个
如今,可输入字符串在render()
中是这样控制的:
<span>{140 - this.state.text.length}</span>
如今,可输入字符数也依赖this.state.photoAdded
,因此咱们须要添加if-else
语句
然而,在JSX中,你不能直接在{...}
写入if-else
语句,你可使用三元运算符,可是那样的话在这个项目里面也太长了。
正常最简单的作法就是写一个方法来判断状态,咱们来尝试一下。
首先,从新编辑span
标签的代码:
<span>{ this.remainingCharacters() }</span>
而且,这样定义方法:
remainingCharacters: function() { if (this.state.photoAdded) { return 140 - 23 - this.state.text.length; } else { return 140 - this.state.text.length; } },
如今,"Add photo"的状态应该能够影响可输入剩余字数了。
问题:在render()
中为何{ this.remainingCharacters() }
后面有()
,而{ this.handleChange }
以及{ this.togglePhoto }
没有呢?
好问题,让咱们再看一次render()
:
render: function() { return ( ... <textarea className="..." onChange={ this.handleChange }></textarea> ... <span>{ this.remainingCharacters() }</span> ... <button className="..." onClick={ this.togglePhoto }> ... </button> </div> );
参考答案:
咱们写remainingCharacters()
这个方法来返回一个数,咱们须要获得这个数并让它在span
标签中显示,因此咱们须要经过()
调用remainingCharacters()
另外一方面,handleChange
以及togglePhoto
是事件处理方法,只有当交互(改变文本或者点击按钮)实现的时候,咱们才须要调用这些方法。因而,咱们不须要写()
,只须要把他们赋值给onChange
以及onClick
这样的属性
Tweet按钮的状态
咱们仍然有一个需求须要完善:
若是"Add Photo"按钮的状态是"on",可是没有字符输入,"Tweet"按钮应该能够启用:
这很简单,以前"Tweet"按钮的禁用属性是这样设置的:
<button ... disabled={this.state.text.length === 0}>...</button>
简而言之,要将以前只要字符输入为0的时候,"Tweet"按钮禁用改为只要:
字符串输入为0
"Add Photo"按钮的状态是"off"
二者都知足的时候"Tweet"按钮禁用
因此逻辑就变成了这样:
<button ... disabled={this.state.text.length === 0 && !this.state.photoAdded}>...</button>
或者,你能够调用remainingCharacters()
来简化代码。若是未输入字符为140,意味着没有文本输入且"Add Photo"按钮的状态为"off",从而"Tweet"按钮应该禁用:
<button ... disabled={this.remainingCharacters() === 140}>...</button>
就是这样的,尝试点击"Add Photo"按钮来检查"Tweet"按钮是否设置正确。
咱们完成了
看起来很是简单,这是完整的代码:
var TweetBox = React.createClass({ getInitialState: function() { return { text: "", photoAdded: false }; }, handleChange: function(event) { this.setState({ text: event.target.value }); }, togglePhoto: function(event) { this.setState({ photoAdded: !this.state.photoAdded }); }, remainingCharacters: function() { if (this.state.photoAdded) { return 140 - 23 - this.state.text.length; } else { return 140 - this.state.text.length; } }, render: function() { return ( <div className="well clearfix"> <textarea className="form-control" onChange={this.handleChange}></textarea> <br/> <span>{ this.remainingCharacters() }</span> <button className="btn btn-primary pull-right" disabled={this.state.text.length === 0 && !this.state.photoAdded}>Tweet</button> <button className="btn btn-default pull-right" onClick={this.togglePhoto}> {this.state.photoAdded ? "✓ Photo Added" : "Add Photo" } </button> </div> ); } }); React.render( <TweetBox />, document.body );
使用React,对于"Add Photo"按钮的代码所作的改变是最小的,不须要进行重构,这是为何呢?
这与React的代码风格思想有很大的关系。在React中,只有当事件发生的时候,state
才发生改变,以后,React自动调用render()
来更新UI。
在这个特殊的例子中,它是这样发生的:
state成为了事件以及render()
之间过渡:
每一个事件不须要担忧哪一部分的DOM发生变化,他们只须要设置state
就能够了
相应的,当你写render()
的时候,你也只须要担忧如今的state
是什么
与jQuery作比较:
你能够想象一下,当UI有更多交互的时候,会发生什么,没有了中间的过渡层'state',咱们须要花费很大的精力来解决它们之间相互的联系。这就是为何对于复杂的组件,建议使用React而不是jQuery。
再次说明,你也能够写出简洁的jQuery代码。可是你必须想出良好的代码结构,每次想要增长新功能的时候还须要特别注意是否影响代码的重构。而使用React,就没有这样的烦恼。
最后一个功能就是超过所限制输入的字符,超出的字符高亮显示:
可是,咱们不会真的在输入框的内部高亮显示,由于这须要咱们把textarea
改为contenteditable
,而contenteditable
在这个示例中处理起来有点复杂。
咱们会在输入框的上面添加一个弹出框,而且显示应该删除的超出字符限制的字符:
你能够尝试copy下面乔布斯的一段话:
If you haven't found it yet, keep looking. Don't settle. As with all matters of the heart, you'll know when you find it. And, like any great relationship, it just gets better and better as the years roll on.
而且复制进下面的JSBin的连接:高亮显示的代码连接
它应该在文本框的上面有一个弹出框,超出字数限制的字符应该红色高亮显示
在红色高亮显示的字符以前应该还有10个字符没有高亮
若是咱们用jQuery写这个功能,咱们的代码会更加的混乱。看下面的图,咱们须要从新增长两个箭头
因此咱们不会用jQuery来实现这样的一个功能。
咱们只会用React来实现它,只须要增长一个箭头就行了:
接着以前的React代码
咱们会一步一步来作。首先,当输入的字符超过限制字数的时候,会弹出一个简单的弹出框,它里面会有一些简单的文字。
由于它须要条件判断,因此咱们须要写一个程序来处理。在文本框以前增长{ this.overflowAlert() }
:
{ this.overflowAlert() } <textarea className="form-control" onChange={this.handleChange}></textarea>
如今,这个方法应该返回:
若是没有字符串遗留,则返回一个div标签
不然,没有任何东西返回
在React中,你能够经过函数方法返回一个JSX标签,而且在其余的方法中使用它。换而言之,你能够尝试作下面的事情:
someMethod: function() { return ( <a href="#">Hello World</a> ); }, someMethod2: function() { return ( <h1> { this.someMethod() } </h1> ); },
在咱们的例子中,咱们能够在一种状况下返回( <div> ... </div> )
,另外一种状况下,什么也不返回。这样咱们的overflowAlert
方法以下所示:
overflowAlert: function() { if (this.remainingCharacters() < 0) { return ( <div className="alert alert-warning"> <strong>Oops! Too Long:</strong> </div> ); } else { return ""; } },
注意,咱们是经过检查this.remainingCharacters()
来判断咱们是否弹出提示框。
尝试在文本框里面输入超过140个字符(或者一张图片+113个字符),它将会弹出提示框。
对超出的字符进行颜色样式的处理
下面是超出字符的处理方式:
在"Oops! Too Long:"和超出的字符之间,存在一个小空格,它在三个'.'以前。我使用了
提示框中有this.state.text
第131到第140个字符
其他的就是红色高亮显示的字符
下面在JSX中修改代码,在overflowAlert
添加一个if
判断,咱们会建立两个变量:beforeOverflowText
以及overflowText
,咱们会在this.state.text
中使用.substring()
方法:
if (this.remainingCharacters() < 0) { var beforeOverflowText = this.state.text.substring(140 - 10, 140); var overflowText = this.state.text.substring(140); return ( <div className="alert alert-warning"> <strong>Oops! Too Long:</strong> ...{beforeOverflowText} <strong className="bg-danger">{overflowText}</strong> </div> ); }
再次copy下面的语句,粘贴进文本框,你就能够看到高亮的文本了,咱们基本上以及完成了。
If you haven't found it yet, keep looking. Don't settle. As with all matters of the heart, you'll know when you find it. And, like any great relationship, it just gets better and better as the years roll on.
若是"Add Photo"按钮的状态是"on"呢?
若是"Add Photo"按钮的状态是"on",那么字符限制就要减小23,因此咱们的beforeOverflowText
以及overflowText
须要考虑到这个状况:
if (this.state.photoAdded) { var beforeOverflowText = this.state.text.substring(140 - 23 - 10, 140 - 23); var overflowText = this.state.text.substring(140 - 23); } else { var beforeOverflowText = this.state.text.substring(140 - 10, 140); var overflowText = this.state.text.substring(140); }
如今,尝试点击"Add Photo"查看显示效果吧~
让咱们来看看改变了什么:
以上就是个人教程,指望你能从中获得:
jQuery代码和React代码的不一样
怎么在JSX中写一些基本的React代码
接下来呢?
个人教程里面仅仅只是包含了React中的最好的部分。接下来你须要学的是:
怎么编写组件
在state
中使用极广的props
幸运的是,官方的教程以及足够好了,你能够好好研读它们。
感谢阅读,若是你有任何的疑问,能够给我发email: shu@chibicode.com
其实距离本身读完这篇文章已经很久了,一直在拖拖拖。做者介绍了React中的瑰宝state
,确实让我受益不少。前先后后翻译这篇文章快一个月了吧。今天才算真正理解了翻译的不易~