Redux教程3:添加倒計時

前面的教程裏面,咱們搭建了一個簡單紅綠燈示例,經過在console輸出當面的倒計時時間;因爲界面上不能顯示倒計時,用戶體驗並不良好,本節咱們就添加一個簡單的倒計時改善一下。html

做爲本系列的最後一篇文章,將示例如何處理多個Redux、React的情形;react

一、建立Counter類

咱們定義倒計時的類名爲 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

在假設用戶已經編寫上面的代碼文件的基礎上,咱們繼續講解如何將 CounterLight 兩個組件聯合起來。less

二、建立入口文件

Redux的三個原則之一 : 單一store,單一reducer 。咱們建立兩個文件,分別整合以前所寫的 reducer 和 store 。dom

2.一、reducer入口文件

建立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

2.二、store入口文件

建立stores/traffic.js文件,做爲 主store 入口文件:

import { createStore } from 'redux'
import rootReducer from '../reducers/traffic'

export default function trafficStore(initState){
    return createStore(rootReducer,initState);
}

能夠看到並無什麼工做量,只是多了幾行代碼而已;

三、建立應用

前面建立的 CounterLight 算是組件,將二者結合起來,能夠視做一款小應用了(假設應用名爲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後查看;

3.一、初始化

app/traffic/index.js 中引入 CounterLight 組件並設置初始化值:

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);
  • 初始化的時候,咱們從綠燈開始;
  • 倒計時的時間來自於 initLight.light.time ,這樣在初始化狀態的時候關聯起來兩個組件
  • 將兩個組件的狀態(initLight,initCount)合併成 initState ,傳給應用的 store,以完成 應用store的初始化

3.二、建立React組件,並連接到Redux

緊接着,使用 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')
)

形式和上篇提到的相似,細節略微有些不一樣:

  • mapStateToProps 中返回的對象有兩個屬性 lightcount ,在React組件中 對應 this.props.light 、 this.props.count
  • mapDispatchToProps 中現將兩個組件的方法先和dispatch綁定,合成一個對象以後再賦值,這樣在React組件中使用 this.props.actions 能夠調用這兩個組件的全部的actions創造函數;
  • 最後使用&lt;Provider&gt;注入 store 實例;

3.三、完善App組件內容

最後,綁定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 方法中

  • 使用 actions.countDown(); 讓倒計時減1,經過 store.getState(); 獲取更新後的狀態,由於若是直接使用 count.num 獲取的是 更新以前 的狀態;
  • curState.count.num 小於 0 的時候,調用 this.changeColor(light,actions); 更改紅綠等的顏色,同時將 新的紅綠燈的time值初始化 Counter 組件,這樣就完成了二者的綁定

3.四、預覽效果

在 http://localhost:3000/app/traffic 中查看效果,效果正如此係列文章第一篇開頭所展現的那樣,紅綠燈搭配倒計時運行:

預覽效果

(這個是gif圖,若是沒動畫請點擊在新窗口打開)

紅綠燈初始狀態是 綠燈5s ,繼而循環 黃燈3s -> 紅燈7s -> 綠燈5s -> 黃燈3s -> ...

就這樣, CounterLight 融洽地結合起來,完美,happy ending~

四、總結

到這裏,Redux 的入門教程算是完結;整個過程下來,你能夠體會獲得,React只須要關注逐漸的展現就好了,全部狀態的管理交由redux便可,這種綁定剛好體現了容器組件和展現組件相分離的開發思想: 只在最頂層組件(如路由操做)裏使用 Redux;內部組件應該像木偶同樣保持「呆滯」,全部數據都經過 props 傳入

這裏須要再強調一下:Redux 和 React 之間沒有關係。Redux 支持 React、Angular、Ember、jQuery 甚至純 JavaScript。

相關文章
相關標籤/搜索