React - Facebook 的 UI 元件 Library
昨天在 TEDxTaipei 實習的時候說要修正之前 Timer (計時器) 的錯誤,我馬上就想到可以用 Facebook 的 React 來改寫。
之前就非常想玩看看,這次完了之後發現確實不錯,可以來推一下 XDD
註:暑假剛好有機會在 TEDxTaipei 實習,主要處理 WordPress 後端一些 PHP Code 和一些與前端搭配的技術,而計時器在 TEDxTaipei 會自行製作是因為有多了幾個特殊的按鈕的關係。
React 是個怎樣的東西,可以在官網的簡介「A JAVASCRIPT LIBRARY FOR BUILDING USER INTERFACES」就很明確地瞭解,是用來做一些 UI 的元件。
說實在的,概念其實很簡單。
- 以類似 DOM 物件的方式描述
- 每個元件都是狀態機
- 能以 XML Like 的方式描述(JSX 功能)
- 樹狀的
在使用 React 上的概念非常的簡單:
- 建立元件( React.createClass )
- 定義行為 ( render / componentWillMount …)
- 組合元件 ( A 元件下的 render 傳回的是一組 B 元件 )
- 渲染元件 ( React.renderComponent( parentComponent, target ) )
我們透過建立一個「元件」來描述外觀 Ex. 內容放在一個 div 之中,然後有著叫做 label 的 className ( 因為是以 JS 撰寫,所以這些屬性都是用 JS 的方式來設定 ) 接著渲染 ( render ) 這個元件,在某一個 DOM 的物件上 Ex. document.body
下面是以 CoffeeScript 所撰寫的 Hello World 版本,因為使用 CoffeeScript 因此和 JSX 不相容。 不過仍能運作的關係是因為 JSX 算是輔助,只要在撰寫時使用「編譯後」的方式呈現即可( JSX Live Compiler 有興趣可以嘗試看看 )
1HelloWorld = React.createClass {
2 render: ->
3 (
4 React.DOM.div {}, [
5 "Hello ",
6 React.DOM.div {className: 'label label-info'}, @props.name
7 ]
8 )
9}
10
11React.renderComponent (HelloWorld {name: "World"}), document.body
Live Demo: https://jsfiddle.net/elct9620/TPHpS/
事實上,一開始我以為 React 有著複雜或者非常詳盡的概念(因為我覺得他和 AngularJS 建立界面有點類似的感覺)
不過事實上其實非常精簡而且簡潔的,這邊先來討論他 DOM Like 的部分。
從上圖來看,我們使用 React 裡面的 DOM 物件產生的 div 物件,或者是自行利用 createClass 產生的 DOM Like 物件(用這種方式稱呼似乎比較好理解),都是同一個類型物件下的產物,差別只在於原本的 DOM 物件沒有 state (狀態) 的屬性,這也是為什麼在 JSX 下會使用 <Hello name=“World” /> 的方式撰寫的原因。
因為 React 先把全部的 DOM 和你用 React 產生的 DOM Like 物件都先抽象化,就個人感覺來說,他讓我不需要寫非常多 inline HTML code 在 JS 裡面,而是純粹的 JS 讓程式碼變得乾淨許多。
假設希望取得原本的 DOM 物件呢?只要對產生出來的物件使用 getDOMNode()
方法即可(通常用於 addEventListener 等綁定事件上)
註:昨天測試似乎無法直接加上 onClick 綁定事件,也許待會會成功
接下來就是 State (狀態) 的問題。
1{button} = React.DOM
2
3Button = React.createClass {
4 getInitialState: ->
5 {btnClass: 'btn-default'}
6
7 handleClick: (event)->
8 newClass = if @state.btnClass is 'btn-success' then 'btn-default' else 'btn-success'
9 @setState {btnClass: newClass}
10
11 render: ->
12 @label = if @state.btnClass is 'btn-success' then 'Success!' else 'Start'
13 (
14 button {className: "btn #{@state.btnClass}", onClick: @handleClick}, @label
15 )
16}
17
18React.renderComponent (Button {}), document.body
Live Demo: https://jsfiddle.net/elct9620/cecjs/
這個範例我建立了一個叫做 Button 的物件,他會渲染出一個 HTML 的 button 元素,而上面套用的 class 樣式以及 Label 則會受到名為 btnClass 狀態的影響而產生改變。
如果仔細看的話,會注意到裡面完全沒有對 UI 作出重新渲染( 呼叫 render() )的動作,但是界面卻自動的產生改變。 因此我們不需要關心「渲染」的問題,只要專注在於一個 UI 的設計。
註:不知道為什麼 onClick 可以用了(感動)他似乎會自動做好 addEventListener 和 removerEventListener 超貼心,我推測 onXXX 的都是這樣使用,官網也有用到 onChange 的屬性。
至於屬性部分,操作起來和 State 基本上是一樣的。 (也可以透過 setProps 方法改變物件屬性,進而觸發重新 render 主要就看設計。)
再來是關於表單地強化功能。
假設我現在需要取得元件內某個欄位的數值該怎麼辦呢?
這時候可以借助對表單元素設置 ref 元素來輔助。
1{input, div, span} = React.DOM
2
3Nickname = React.createClass {
4 getInitialState: ->
5 {nickname: @props.nickname}
6 handleChange: (event)->
7 @setState {nickname: @refs.nickname.getDOMNode().value.trim()}
8 render: ->
9 (
10 div {}, [
11 input {ref: 'nickname', className: 'form-control', onChange: @handleChange},
12 div {}, [
13 span {className: 'label label-success'}, 'Nickname'
14 ],
15 " : "
16 ,
17 @state.nickname
18 ]
19 )
20}
21
22React.renderComponent (Nickname {nickname: "Wade"}), document.body
Live Demo: https://jsfiddle.net/elct9620/ZrHy8/
在這段 Code 可以看到我對 input 設定了一個 ref 屬性,並且能夠在 onChange 事件發生時透過 @refs 來讀取對應的元素。
註:在 React 中支援 trim() 協助清除空白,這邊嘗試使用 props 但是會發生錯誤,因此建議 props 只適合在初始化時使用
那麼 React 適合被使用嗎?這是我在 Facebook 留言區塊發現的程式碼,每個使用 React 產生的元素都會有一個叫做 data-reactid 的屬性,用來辨識。
所以,趕緊來試玩看看吧!使用起來其實也非常順暢。