前面的教程裏面,咱們搭建了一個簡單紅綠燈示例,經過在console輸出當面的倒計時時間;因爲界面上不能顯示倒計時,用戶體驗並不良好,本節咱們就添加一個簡單的倒計時改善一下。html
做爲本系列的最後一篇文章,將示例如何處理多個Redux、React的情形;react
咱們定義倒計時的類名爲 Counter ,建立所須要的文件(夾):git
mkdir actions/counter reducers/counter stores/counter components/counter views/counter touch constants/Counter.js actions/counter/index.js reducers/counter/index.js stores/counter/index.js components/counter/index.js components/counter/redux.js components/counter/index.less components/counter/demo.js views/counter/index.hbs
建立 Counter 的 Redux 和 React 組件的過程就至關於重複了一下以前兩篇文章的過程,代碼也不復雜,我這邊也就不粘貼了。可自行參考代碼,代碼託管在 https://github.com/boycgit/demos/tree/master/traffic;github
能夠經過 http://localhost:3000/counter/redux 檢驗是否正常運行;redux
(這個是gif圖,若是沒動畫請點擊在新窗口打開)app
在假設用戶已經編寫上面的代碼文件的基礎上,咱們繼續講解如何將 Counter 和 Light 兩個組件聯合起來。less
Redux的三個原則之一 : 單一store,單一reducer 。咱們建立兩個文件,分別整合以前所寫的 reducer 和 store 。dom
建立reducers/traffic.js
文件,做爲 主reducer 入口文件:ide
import { combineReducers } from 'redux' import light from './light/' import count from './counter/' const rootReducer = combineReducers({ light, count }); export default rootReducer
這裏包含了最佳實踐法則, 將不一樣的狀態轉移關係寫進不一樣的js文件,最後彙總到 index.js 中(這裏名爲traffic.js,地位是同樣的) ,好比後期若是多出一種 「汽車的狀態轉移」 關係,只要新建對應的js文件,而後再在index.js中的combineReducers
函數中多添加一行配置便可;函數
詳細的概念及做用請參考Redux的中文文檔Reducer
建立stores/traffic.js
文件,做爲 主store 入口文件:
import { createStore } from 'redux' import rootReducer from '../reducers/traffic' export default function trafficStore(initState){ return createStore(rootReducer,initState); }
能夠看到並無什麼工做量,只是多了幾行代碼而已;
前面建立的 Counter 和 Light 算是組件,將二者結合起來,能夠視做一款小應用了(假設應用名爲traffic
);
爲了方便管理,專門建立 App 文件夾來存放應用,並建立應用相關的等輔助內容(好比視圖等):
mkdir app app/traffic views/app touch app/traffic/index.js app/traffic/index.less views/app/index.hbs views/app/traffic.hbs
核心是 app/traffic/index.js 文件,其他文件只是其輔助做用,這邊也不重點講解,可自行到git clone後查看;
在 app/traffic/index.js 中引入 Counter 和 Light 組件並設置初始化值:
import React, {Component, PropTypes} from 'react' import {render} from 'react-dom' import { Provider, connect } from 'react-redux' import { bindActionCreators } from 'redux' import * as LightActions from '../../actions/light/' import * as CounterActions from '../../actions/counter/' import Light from '../../components/light/' import Counter from '../../components/counter/' import trafficStore from '../../stores/traffic' // 初始化狀態 let initLight = { light:{ color:'green', time:'5' } } let initCount = { count:{ num : parseInt(initLight.light.time) } } let initState = Object.assign({},initLight,initCount); // 聲明store let store = trafficStore(initState);
緊接着,使用 connect 方法連接 Redux 和 React組件:
class App extends Component{ // 佔位 } // 聲明 connect 鏈接 // 將 redux 中的 state傳給 App function mapStateToProps(state){ return{ light:state.light, count:state.count } } // 綁定多個actions function mapDispatchToProps(dispatch){ let boundLight = bindActionCreators(LightActions,dispatch); let boundCount = bindActionCreators(CounterActions,dispatch); return{ actions : Object.assign({},boundLight,boundCount) } } // 聲明 connect 鏈接 App = connect(mapStateToProps,mapDispatchToProps)(App); // 真正的鏈接 render( <Provider store={store}> <App /> </Provider>, document.getElementById('demo') )
形式和上篇提到的相似,細節略微有些不一樣:
dispatch
綁定,合成一個對象以後再賦值,這樣在React組件中使用 this.props.actions 能夠調用這兩個組件的全部的actions創造函數;<Provider>
注入 store 實例;最後,綁定store以後完善 App類 的代碼,大部分的邏輯和前一篇的相似:
class App extends Component{ _bind(...methods){ methods.forEach((method)=>this[method] = this[method].bind(this)); } constructor(){ super(); this._bind('changeColor','handleClick','autoChange'); this.state = { timeId : null } } changeColor(light,actions){ // 紅路燈變換規則 switch(light.color){ case 'red': actions.changeGreen(); break; case 'green': actions.changeYellow(); break; case 'yellow': actions.changeRed(); break; default: actions.changeRed(); } } autoChange(){ // 自動更改紅綠燈 const { light,count, actions } = this.props; let _self = this; actions.countDown(); let curState = store.getState(); if(curState.count.num < 1){ this.changeColor(light,actions); curState = store.getState(); actions.countInit(parseInt(curState.light.time)); } // 自動更改 this.state.timeId = setTimeout(function(){ _self.autoChange(); },1000); } handleClick(e){ // 用點擊模擬紅路燈 if(this.state.timeId){ clearTimeout(this.state.timeId); this.state.timeId = null; } else { this.autoChange(); } } render(){ // 經過connect 注入 redux 的 dispatch 方法 const { light,count, actions } = this.props; return ( <div id="traffic" onClick={this.handleClick}> <Light light={light}/> <Counter num={count.num}/> </div> ) } } // 聲明 connect 鏈接
變換的邏輯都在 autoChange 方法中
在 http://localhost:3000/app/traffic 中查看效果,效果正如此係列文章第一篇開頭所展現的那樣,紅綠燈搭配倒計時運行:
(這個是gif圖,若是沒動畫請點擊在新窗口打開)
紅綠燈初始狀態是 綠燈5s ,繼而循環 黃燈3s -> 紅燈7s -> 綠燈5s -> 黃燈3s -> ...
就這樣, Counter 和 Light 融洽地結合起來,完美,happy ending~
到這裏,Redux 的入門教程算是完結;整個過程下來,你能夠體會獲得,React只須要關注逐漸的展現就好了,全部狀態的管理交由redux
便可,這種綁定剛好體現了容器組件和展現組件相分離的開發思想: 只在最頂層組件(如路由操做)裏使用 Redux;內部組件應該像木偶同樣保持「呆滯」,全部數據都經過 props 傳入 。
這裏須要再強調一下:Redux 和 React 之間沒有關係。Redux 支持 React、Angular、Ember、jQuery 甚至純 JavaScript。