Redux 初步嘗試

寫文章的時候仍是 1.0.x, 如今已經 3.x 了.
雖然主體 API 沒改, 可是細節的 API 增長了不少, 甚至更簡單的方案.html


關注 Redux 好久了, 一直在等穩定版, 終於穩定版出來了
不過真的運行起來, 比我以前估計的複雜度高太多了
這邊能夠看我用 CirruScript 寫的代碼... 雖然效果是不怎麼樣
https://github.com/jiyinyiyong/redux-in-cirrureact

大概梳理下這兩天遇到的東西, 爲後面作準備git

關於

關於 Redux 我遇到的中文社區已經有兩篇文章, 還行
https://ruby-china.org/topics/26944
http://segmentfault.com/a/1190000003033033
另外中文文檔也有同窗在翻譯, 速度飛快啊:
https://github.com/camsong/redux-in-chinese
其餘大量關於 Redux 的資源, 在列表能找到, 熱度很高的
https://github.com/xgrommx/awesome-reduxgithub

要開始寫 Redux 的話, 其實文檔是分佈在三個倉庫當中的:
https://github.com/gaearon/redux
https://github.com/gaearon/redux-devtools
https://github.com/rackt/react-redux
其中 redux-devtools 是調試工具, 也是一個 React 組件
這個組件須要在開發環境判斷渲染, 同時避免發佈到線上去
而 react-redux 則是對於 React 的綁定, 包含了一些工具函數編程

我瞭解得不大具體, 其中 react-devtools 文檔並不齊全
甚至須要去源碼當中看具體的例子才能把 Demo 跑起來:
https://github.com/gaearon/redux-devtools/blob/master/examples/counter/containers/App.js
而 Redux 具體的寫法, 也少不了要去看源碼的 example 纔算能夠
https://github.com/rackt/redux/tree/master/examplesredux

combineStore 和綁定 props 要注意

Redux 原來給出的概念, 聽起來很簡單的, Store 部分很像 Elm
大體就是 Model 部分設計成爲一個不可變數據, 而後渲染
然而實際狀況好像要複雜一些, 目前的 Store 當中的數據不是這樣的
按照文檔, 通常會出現這樣的寫法 combineReducers
http://rackt.github.io/redux/docs/basics/Reducers.html
注意是 ES6 的對象, 省略了 property 的書寫, 其實是個 Object 的定義:segmentfault

import { combineReducers } from 'redux';

const todoApp = combineReducers({
  visibilityFilter,
  todos
});

這個方案的問題就是, Store 的頂層數據, 實際上是用 Object 模擬的
包括後邊綁定數據到組件上, 也是用了其中的一些 trick
好比說有這樣的代碼, 用來指明 store 傳入的數據怎樣綁定到組件上
https://github.com/rackt/redux/blob/master/examples/counter/containers/CounterApp.jsapi

function mapStateToProps(state) {
  return {
    counter: state.counter
  }
}

function mapDispatchToProps(dispatch) {
  return bindActionCreators(CounterActions, dispatch);
}

export default connect(mapStateToProps, mapDispatchToProps)(Counter);

如今 API 按說已經穩定, 可是個寫法仍是致使結果稍微複雜了一些
按做者說, Splitting Reducers, 化大爲小, 是管理 Store 比較好的辦法
但我總以爲應該是從不可變數據自己去找, 而不是增長一套寫法
也許之後文檔上或者教程上會說得明確一些, 如今我還不明白ruby

細節要注意區分一下, 並且要按照代碼跑一跑才行, 我描述得不清楚
因爲上邊這個結構的緣由, store 自己定義的方法, 也許不方便直接用ide

Provider 的寫法

關於把 Store 的數據傳遞到組件當中, Redux 提供了額外的綁定
結果也帶出來了 Provider 組件, 接收屬性, 還接收函數做爲參數, 寫法是:
https://github.com/rackt/redux/blob/master/examples/counter/containers/Root.js
注意, CounterApp 這邊沒寫屬性, 是經過簽名提到的寫法注入進去的
大體上是 mapStateToProps 函數, 具體細節恐怕須要看源碼:

import { Provider } from 'react-redux';

export default class Root extends Component {
  render() {
    return (
      <Provider store={store}>
        {() => <CounterApp />}
      </Provider>
    );
  }
}

而 Provider 的概念負責的事情彷佛也多了一些, 具體到文檔上看
http://rackt.github.io/redux/docs/basics/UsageWithReact.html

實現一個最簡單的 Redux 應用, 須要的代碼:
https://github.com/jackielii/simplest-redux-example/blob/master/index.js

Middlewares

中間件的概念我沒看懂, 只是大體抄了一遍代碼嘗試了一遍
思路是用高階函數對 store 作了一些封裝, 插入了一些 Action 的操做
http://rackt.github.io/redux/docs/advanced/Middleware.html

DevTools

前面提到了 Devtools 是用 React 組件的方式提供的
沒找到詳細的文檔, 具體的例子我查看代碼的 examples 才知道的
https://github.com/gaearon/redux-devtools/blob/master/examples/counter/containers/App.js

export default class App extends Component {
  render() {
    return (
      <div>
        <Provider store={store}>
          {() => <CounterApp />}
        </Provider>
        <DebugPanel top right bottom>
          <DevTools store={store}
                    monitor={LogMonitor} />
        </DebugPanel>
      </div>
    );
  }
}

做者說調試工具是能夠定製的, 由於僅僅是 React 組件而已
我估計大概是 LogMonitor 組件能夠本身定義的關係

顯示不可變數據

顯示同時工具以後, 查看數據默認當作 JSON 對象處理和顯示的,
在調試工具當中查看不可變數據稍微要加上一些代碼:
https://github.com/gaearon/redux-devtools/issues/51

let selectDevToolsState = (state = {}) => Immutable.fromJS(state).toJS();

<DebugPanel top right bottom key="debugPanel">
    <DevTools store={store} select={selectDevToolsState} monitor={LogMonitor} />
</DebugPanel>

其中 state 變量有可能爲 undefined 的, 注意不要忘掉處理

純函數 Reducer

我在寫 Demo 時候剛開始寫了 shortid.generate()生成 id, 遇到個 bug
緣由是這個生成 id 的函數是在 reducer 內部運行的,
彷佛因爲 DevTools 的存在, reducer 會被調用不少次, id 被建立了不少次
這不奇怪, 由於 Time Travel Debugger 就是會從新運行 Action 的

因此我才反應過來, 建立 id 在 Haskell 裏也是跟 IO 有關的反作用函數
隨機數還有讀取外部環境的狀態, 屬於反作用, 會破壞純函數
這個代碼是不該該在 reducer 當中的寫的, id 就放 Action Creator 裏去了

這個可能一看暗示了 FRP 那樣的編程思路引出的一個更深入的問題
平時咱們說 MVC, Model 是整個數據的核心, Model 能夠被改變
在 FP 當中, Model 是以變化數據的 Stream 模擬它隨着時間的改變
而這裏, Store 做爲 Model 倒是因變量, 距離核心還隔着一步
數據的核心其實是 initialState, 以及 Action 造成的 Stream
而 Model 其實是經過 initialState 和 Actions 不斷計算出來的

bindActionCreators

Redux 的例子當中, 處理 Action 是經過綁定到組件 props 來傳遞的
而不是我此前採用的, 直接用一個模塊去調用的寫法. 具體寫法看這邊:
https://github.com/rackt/redux/blob/master/docs/api/bindActionCreators.md
不清楚利弊. 我只是以爲這樣設計太複雜了一些

總結

這篇文章算不上教程, 而是初步嘗試 Redux 留下來的一些 Tips我以爲你們關注 Redux 應該都是爲的 Time Travel Debugger 的能力如今看來 Redux 帶來過多概念, 給咱們項目跟進形成了門檻整體思路上 Redux 比 Facebook 的方案清晰, 細節還期待更靈活一些

相關文章
相關標籤/搜索