Redux story-1:who creates it?

ne_smalltown發表於2017-01-19

前言

 這是一個系列文章,旨在分享在react及相關技術棧實踐過程中的個人感悟,心得。如果有不好的地方,歡迎各位批評指正。

 由於對react本身還未深入瞭解,今天我們先來談一談redux相關的問題。

Who creates it?

Dan Abramovredux的作者,同時,他也是Create React App, React Hot Loader作者。當然1年前,他也由於redux及相關的開源貢獻加入了facebook(他大二就輟學了,之前還當過.net工程師)。

 在我最初瞭解到他的時候,我覺得他非常有禮貌。同時,也為了更多的瞭解redux,我計劃開始閱讀他的每一條tweet,原先計劃的是從15年7月開始,後來因為進展緩慢,而且react版本也已經發生很大變化了,於是便從16年1月1日開始閱讀,目前記錄到7月15日了。事實也證明,在這個過程中,的的確確學習到了很多東西。包括redux的文件及redux-links的作者Mark Erikson,以及國外很多寫過redux系列的朋友們。

 如果你有興趣的話,可以看看我摘錄的一些片段。其中除了知識性的內容外,還有一些關於它自己生活,經歷,學習方法,如何面對JS疲勞等等的摘錄。也讓我漸漸的瞭解到了國外的程式設計師們的一些觀點,興趣,梗等等。

正文

 好了,暫時先介紹到這裡了。切回redux本身,下面是學習原始碼過程中自己的一些體會。

createStore

createStore的第3個引數為enhancer,如果enhancer有多個,那麼應該使用compose的方式組合多個enhancer

 且每個enhancer的模板為export default createStore => (reducer, preloadedState, enhancer) => {...}

 因為在createStore中執行了:return enhancer(createStore)(reducer, preloadedState)

 另外,上面的提到的形如(reducer, preloadedState, enhancer) => {...} 這個樣子的其實都可以叫做createStore

 這也是社群有那麼多enhancer的原因,他們可以形成一個enhancer鏈,我呼叫你的createStore,然後返回我的createStore供下一級呼叫

 所以在自己的createStore的函式體中經常能看到諸如var store = createStore(reducer, preloadedState, enhancer);這樣的用法,目的就是讓自己這一級之前的enhancer產生一個store出來,而之前的enhancer裡的createStore又會呼叫之前的,到最盡頭,就是redux本身的createStore

applyMiddleware

applyMiddleware的目的是返回一個enhancer,這個enhancer儲存了1個或者多箇中介軟體,中介軟體在上一級的dispatch方法的基礎上增添自己的邏輯,然後返回自己的dispatch方法

 對於中介軟體而言,中介軟體的模板為:export default store => next => action => {...}。有的地方也寫成export default _ref => next => action => {...}或者export default ({getState, dispatch}) => next => action => {...},看自己喜好了

 實際的呼叫順序如下(定義在reduxapplyMiddleware.js中):

1. middleware(middlewareAPI);
/*
    var middlewareAPI = {
      getState: store.getState,
      dispatch: (action) => dispatch(action)
    };
    
    chain = middlewares.map(middleware => middleware(middlewareAPI));
    dispatch = compose(...chain)(store.dispatch);

    第1步即為第1次執行中介軟體,用redux自己的dipatch初始化各個中介軟體裡的dispatch(也就是中介軟體的next引數)和getState。
    從而確保至少redux本身能夠正常工作,中介軟體的store或者_ref即為這裡的middlewareAPI
* */

2. dispatch = compose(...chain)(store.dispatch);
/*
    第2步即為第2次執行中介軟體
    即用compose的形式鏈式呼叫第1步返回的中介軟體集合,如果中介軟體是定義在applyMiddleware的最後一個
    那麼中介軟體裡的next為store.dispatch,否則next為上一個中介軟體返回的結果,可以理解為上一個中介軟體
    返回的是封裝了dispatch的自己的dispatch,這裡的原理其實和enhancer一模一樣

    enhancer的目的是封裝多次createStore並用compose的方式進行呼叫
    middleware的目的是封裝多次dispatch並用compose的方式進行呼叫
* */

總結:
/*
    所以最後在redux的createStore.js中return的enhancer(createStore)(reducer, preloadedState)的結果就是一個增強
    版的store,而這個增強版的store中存放的是增強版的dispatch
* */

/* ××××××××××××××××關於combineReducers×××××××××××××××
*   從執行上來說,combineReducers實際上最後就是變成對reducers進行深度優先遍歷並執行的過程
*   從結構上來說,combineReducers決定了我們的state狀態樹的最終結構或者說形狀,他是呈一個樹型結構的
*   combineReducers(reducerA, reducerB),reducerA裡面巢狀combineReducers(reducerA-child1, reducerA-child2)
*   實際上對應狀態樹而已就是第一層有兩個節點A,B,而A節點下面有兩個子節點A-child1,A-child2
*
*   所以在最初設計的時候,我們要設想我們最終的狀態樹的樣子,然後合理劃分reducer,就像設計資料庫的表結構一樣。
    當然這是比較概括的說法,事實上reducer的設計或者說state的劃分有太多太多值得研究的東西,這個我們以後再談了。
* */

bindActionCreator

bindActionCreator實際就是給actionCreator外層再新增了一層函式,而這層函式存放了對dispatch的引用

function bindActionCreator(actionCreator, dispatch) {
  return (...args) => dispatch(actionCreator(...args))
}

所以我們可以一般在元件裡直接呼叫bindActionCreator返回的actionCreator,即this.props.loadSomething(...)。而不用寫成dispatch(actionCreator(...args)),實際上他們是等價的

connect

 既然提到了redux,由於目前我是採用react進行開發,所以不得不提到相關的react-redux。其中最重要的莫屬於connect這個函式了。

 傳入connect的元件在掛載到頁面上後會呼叫store.subscribe進行訂閱,訂閱的目的是我們呼叫dispatch的時候,表明我們的狀態樹即將發生變化,這個時候我們希望我們的元件對應發生變化,而元件變化的唯一方式就是setState

 訂閱就是告訴redux,這個元件是依賴於狀態樹的某部分工作的,所以當你變化的時候,記得獲取最新的state,然後通知我,至於我如何響應,那就是我自己的事了,你只管通知我狀態樹發生了變化並把它傳給我就行了。值得一提的是,connect內部進行了大量的效能優化,避免不必要的渲染,關於此以及mapStateToPropsmapDispathToProps,我們放到以後再談。

結語

 篇幅有限,這一篇文章暫時就先這樣啦,更多的內容,我想放在下一篇來分享,同時自己也在不斷學習,希望能理解得更好。

 值得一提的是,我們也許會認為我們瞭解到的redux,mobx,rxjs等等完全不同理念的庫,他們的作者也許也是”極端”的,是排斥他人及理念的。實際上,這是不正確的,早在16年5月,Dan就和mobx的作者在twitter上有過互動,他們達成了共識,那就是和對方一起合作,一起推動自身以及react的發展。

 對於redux-thunk,文件中也許會首先建議使用這個簡單的庫來處理非同步相關的問題。對於複雜的應用,他們也推薦使用redux-saga這樣的庫去重構自己的程式碼。在twitter上,Dan也多次提到過庫的應用場景的問題,建議大家用之前先了解自己為什麼要使用,它解決了哪些痛點,然後再去使用。甚至特意提過issue,來了解react-router-redux的作用。

 除此之外,也提到在時間充裕的情況下,學習react,應該先從本身入手,ES6,webpack,jsx,redux等等和react本身都是沒有直接聯絡的,在學習完react之後,我們知道了他本身的哪些不足,哪些地方需要加強,哪些地方需要引入第三方庫去解決,解決的是哪些痛點,我們再去了解這些工具,才能真正體會到他們的威力。

 說到這裡,稍微有一點遠了,不過我覺得還是有必要提及一下。那就是,我們身處一個浮躁的社會,無論是在現實中對待朋友,親人,陌生人,由於學習,工作,生活的壓力,周遭的浮躁氛圍的影響,多多少少也會讓自己帶著些許暴戾之氣。在網路上,由於約束的放寬,我們也許更會將壓抑的情感釋放給廣袤的網路世界,在微博,貼吧,知乎上,我們或多或少書寫著,察覺著這樣的行為。

 但是,作為一名程式猿,我還是期待能夠看到我們這個圈子更多的將時間,精力,努力花費在對現有技術的改進,對未知世界的探索,追尋程式,庫,框架,思想的本質,結交志同道合的朋友,一起交流,分享,思考對技術的看法。而不是捲入無休止的撕逼,用了某某而產生的優越,甚至借貶低他人來抬高自己。

 我們可以理解一時的憤懣之情,因為我們大多,真是隻是普普通通的社會人,喜怒哀樂再平常不過。但若我們一直保持這種狀態,永遠在上面這些場景都留下對人不對事的話語,譏諷,甚至謾罵。希望大家能為我們的後代想想。

相關文章