這是一篇國外上了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
,確實讓我受益不少。前先後後翻譯這篇文章快一個月了吧。今天才算真正理解了翻譯的不易~