摘要:[React]React.JS學習筆記(6) - Flux pattern 下的Ajax call
因為Flux這個Pattern[架構]只有定義概略性的方向,在真正進行React開發的時候,很容易會遇到程式碼該放到甚麼地方的問題。例如透過Ajax取得資料這段程式碼,該放到甚麼位置,似乎就有不同的做法。
類似的情境是這樣子,目標是要透過台北市政府資料開放平台的台北市公園景點API取得資料,然後顯示資料在畫面上。要注意到,我們呼叫Web API取值,通常都是使用非同步的模式。也就是說開始呼叫,一直到回傳結果,是有一段時間差的。Flux的架構本質上支持這種非同步的概念,因為使用了Publish/Subscribe pattern,所以可以很直觀的拆解Ajax call為Ajax執行中及Ajax執行成功這兩個動作。
對照底下Flux的概念圖,可以很快地知道我們要建立一個Controller View用來顯示資料,建立一個Action代表取得資料的動作,建立一個Store用來控管資料的儲存及商業邏輯。那麼,我們Ajax取資料的程式碼該放到甚麼地方呢?
放到Controller View
直接把Ajax的處理放到Controller View不是一個好的做法,有一個Package => react-async就是用這種概念,它提供了簡便的方式叫用async的function,並且把得到的資料放到state中。不過,在React中,View的存在是為了在任何時間點,依據資料的變化而顯示UI元素。它是stateless的,也就是只有單一狀態,不需要做狀態切換。把Ajax的呼叫放在產生(render)UI元素的邏輯中,可能會導致render邏輯需要另外再處理其他問題,例如呼叫Ajax失敗時的處理。這會讓React Component不再是stateless。
放到Action Creator
放到Action Creator是最普遍的做法,Action Creator會產生Action,透過Dispatcher讓對Action有興趣的Store做進一步的處理。
function getDataTaipei() {
dispatch({type: "DATA_LOADING"});
$.ajax({
url: "http://data.taipei/opendata/xxxxxxx",
success: function (data, textStatus, jqXHR) {
dispatch({type: "GETDATA_SUCCESS", content: data});
},
error: function(xhr, ajaxOptions, thrownError) {
dispatch({type: "GETDATA_ERROR", error: thrownError});
}
});
}
Ajax call放在Action Creator中,隨著資料正確回傳或是發生錯誤,也會對應產生兩個Action- GetDataSuccess 或 GetDataError,在Action Creator中產生Action是很直覺的邏輯。不過,這也表示說Action Creator需要知道要抓什麼資料,也就是說會有部分商業邏輯存在Action Creator之中。以上面的例子來說,如果需要建立快取機制的話,那麼Action Creator還要知道目前有哪些資料已被快取在Store中。這也就表示Action Creator還需要存取Store,因為資料都是存放在Store中,而這違反了Flux的單向資料流原則。
放到Store
那麼放到Store中呢?看起來一切都合理,所有的商業邏輯都集中在Store中,不過這也代表著Store扮演處理Action角色,也扮演產生Action的角色。這也會讓Store的角色變得較為複雜,雖然副作用可能不大,但總是不符合原先Flux Pattern的預期及規劃。
總之,目前似乎尚無最好的做法,只有較為適合自己情境的做法。Pattern是用來解決類似情境的問題,且需要隨著問題而做出調整,不能死守著不放。目前我是把Ajax Call放在Action Creator中,因為處理到的邏輯尚且單純,沒有Actoin Creator存取Store的狀況出現,所以完全符合Flux的精神。另外,Flux官方的example-Chat中似乎也是以這種方式實作。他們拉出另一個角色-Server連結到Action Creator,如下圖。
若有其他想法及建議,歡迎留言討論,謝謝。
參考: