Redux教程2:連接React

經過前面的教程,咱們有了簡單的環境,而且能夠運行Redux的程序,也對 如何編寫Redux示例 有了初步的印象;css

掌握了 使用Redux控制狀態轉移 ,繼而驅動 React 組件發生改變,這纔是學習Redux的初衷。html

本篇咱們將 Redux 和 React 聯合起來,着重講解redux-react模塊的使用;react

一、編寫紅綠燈React組件

在原有的基礎上,咱們編寫紅綠燈組件:webpack

touch components/light/index.js components/light/index.less

components/light/index.js 中寫React代碼,其結構很是簡單:git

import React, { PropTypes, Component } from 'react'
import { render } from 'react-dom'
import classnames from 'classnames'
import './index.less'

class Light extends Component{
    render(){
        let color = this.props.light.color;
        return(
            <div className="traffic-light">
                <span className={classnames('light',color)} />
            </div>
        )
    }
}

Light.propTypes = {
    light: PropTypes.object.isRequired
}

Light.defaultProps = {
    light : {color:'red',time:'4'}
}

export default Light

根據更改樣式類名('red'、'green'、'yellow'),從而移動 sprite圖 產生燈變換的效果:es6

.traffic-light{
  .light{
    display: inline-block;
    background: url(//lh3.googleusercontent.com/-YWLqWZXDYHU/VmWC7GHoAuI/AAAAAAAACgk/nXvEmSWAhQU/s800/light.png) no-repeat 0 0;
    background-size: auto 100%;
    overflow: hidden;
    width:140px / 2;
    height:328px / 2;

    &.red{
      background-position: 0,0;
    }
    &.yellow{
      background-position: -78px , 0;
    }
    &.green{
      background-position: -156px , 0;
    }
  }
}

修改 components/light/demo.js 文件代碼爲:github

import React, {Component, PropTypes} from 'react'
import {render} from 'react-dom'
import Light from './index'

var color = 'red';

render(
    <div id="traffic">
        <Light color={color}/>
    </div>,
    document.getElementById('demo')
)

這樣就能經過 http://localhost:3000/light/demo 預覽這個組件了;web

demo light

二、連接React和redux

有了React和以前的Redux,如今就要將二者連接起來了。咱們的目標是讓紅綠燈運行起來,就比如平時在十字路口看到的那樣;npm

2.一、建立示例文件

再建立一個示例文件,就不叫demo了,叫作redux好了:redux

touch components/light/redux.js

之因此示例文件名稱爲demo.jsredux.js,是由於我在 webpack.config.js 中配置了,若是想用其餘的文件名,只要依樣畫葫蘆就能夠;

首先在 components/light/redux.js 中輸入最基本的腳手架代碼,引入所須要的組件或模塊:

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 lightStore from '../../stores/light/'
import Light from './index'

// 聲明store
let store = lightStore();

2.二、建立容器React

繼而建立一個 App React類 ,做爲總的容器,將上述的 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 lightStore from '../../stores/light/'
import Light from './index'

// 聲明store
let store = lightStore();

class App extends Component{
    _bind(...methods){
        methods.forEach((method)=>this[method] = this[method].bind(this));
    }
    constructor(){
        super();
        this._bind('autoChange','handleClick');
        this.state = {
            count : 0,
            timeId : null
        }
    }

    autoChange(){ // 自動更改紅綠燈
        var _self = this;

        // 這裏放置邏輯代碼

        this.state.timeId = setTimeout(function(){
            // 遞歸調用,實現 setInterval 方法
            _self.autoChange();
        },1000);
    }
    handleClick(e){  // 用點擊模擬紅路燈

        if(this.state.timeId){
            clearTimeout(this.state.timeId);
            this.state.timeId = null;
        } else {
            this.autoChange();
        }

    }
    render(){
        // 經過connect 注入 redux 的 dispatch 方法
        return (
            <div id="traffic" onClick={this.handleClick}>
                <Light light={'yellow'}/>
            </div>
        )
    }
}

上面的代碼仍是個半成品,看不到效果;簡單描述一下上面的代碼作了什麼:

  • 定義App容器,將 Light 組件放在其render方法中
  • constructor 方法引用了 *_bind* 方法,方便一次性綁定this上下文,該方法來自文章Refactoring React Components to ES6 Classes
  • handleClick 方法是純粹是爲了演示,當用戶點擊紅綠燈的時候,紅綠燈調用 autoChange方法 開始自動變換,用戶再次點擊的時候就中止變換;
  • autoChange 方法用於紅綠燈狀態自動轉換的,這裏佔位;本質是使用setTimeout代替setInterval實現;

2.三、連接React組件和Redux類

這是最爲關鍵的一個步驟,

class App extends Component{

    ...
}

// 聲明 connect 鏈接
// 將 redux 中的 state傳給 App
function mapStateToProps(state){
    return{
        light:state
    }
}

function mapDispatchToProps(dispatch){
    return{
        actions : bindActionCreators(LightActions,dispatch)
    }
}

// 聲明 connect 鏈接
App = connect(mapStateToProps,mapDispatchToProps)(App);

// 真正的鏈接
render(
    <Provider store={store}>
        <App />
    </Provider>,
    document.getElementById('demo')
)

這裏使用 react-redux 提供connect的方法 連接React組件和Redux類

// 聲明 connect 鏈接
App = connect(mapStateToProps,mapDispatchToProps)(App);
  • connect 方法不會改變原來的組件類,反而返回一個新的 已與 Redux store 鏈接的 組件類。注意這裏並無注入store對象,真正store對象的注入靠最後的&lt;Provider store&gt;組件;(更多說明請參考 [react-redux 的 API][1])
  • 傳入 connectmapStateToProps方法 ,正如其名,是將 Redux 的狀態 映射到 React組件的props屬性。任什麼時候候,只要 Redux store 發生改變,mapStateToProps 函數就會被調用。這裏返回對象是{light:state},這樣確保 Redux 中的 state 發生改變時,組件的 props.light 都是最新的 Redux state。
  • mapDispatchToProps方法 則是將 Store 中的 dispatch方法 直接封裝成對象的一個屬性,通常會用到 Redux 的輔助函數bindActionCreators();這裏將dispatch綁定到action屬性,這樣在紅綠燈組件內讓其變成紅燈的時候,不須要dispatch(changeRed())這麼調用,直接使用actions.changeRed(),語義化更好;(更多說明請參考 [react-redux 的 API][1])
  • 最後的&lt;Provider store&gt;使組件層級中的 connect() 方法都可以得到 Redux store ,這裏才真正注入store變量,以前的只是聲明而已(以前的比如store是個形參,到了這一步store就是實參了)。(更多說明請參考 [react-redux 的 API][1])

通過上面的語句,Redux就將 state屬性 、 (store 的)dispatch方法與 React 組件的 props 綁定在一塊兒,凡是更改 redux 的 states,就會更新所鏈接組件的props屬性。

react-redux 中的 connect 方法就算是HOC(High Order Component,高階組件)了,具體原理可參考文章初識React中的High Order Component,這是由於若是使用ES6 寫React組件的話,mixin是不支持的,所以使用High Order Component代替;

2.四、利用redux驅動react

理解了最爲困難的部分,以後的事情就水到渠成了;

如今,只要記住 在App中能夠直接使用Redux中的一切了 就好了

咱們回過頭來,完善App組件的代碼,完善 autoChange 方法:

class App extends Component{
    _bind(...methods){
        methods.forEach((method)=>this[method] = this[method].bind(this));
    }
    constructor(){
        super();
        this._bind('changeColor','handleClick','autoChange');
        this.state = {
            count : 0,
            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, actions } = this.props;
        let _self = this;

        let curCount = ++this.state.count;

        // console.log('xx,',curCount);
        if(this.state.count > +light.time){
            curCount = 0;
            this.changeColor(light,actions);
        }
        // 自動更改
        this.state.timeId = setTimeout(function(){
            _self.setState({count:curCount});
            _self.autoChange();
        },1000);

    }
    handleClick(e){  // 用點擊模擬紅路燈

        if(this.state.timeId){
            clearTimeout(this.state.timeId);
        } else {
            this.autoChange();
        }

    }
    render(){
        // 經過connect 注入 redux 的 dispatch 方法
        const { light, actions } = this.props;
        return (
            <div id="traffic" onClick={this.handleClick.bind(this)}>
                <Light light={light}/>
            </div>
        )
    }
}

至此已經完成本節示例,經過npm start開啓服務, 在 http://localhost:3000/light/redux 中查看。

在這個示例裏,經過點擊紅綠燈,每隔若干秒紅綠燈就會變換顏色,這說明二者已經連接起來;

demo light

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

在後一篇文章,將示例如何處理多個Redux、React的情形;

[1][http://camsong.github.io/redux-in-chinese/docs/react-redux/api.html](http://camsong.github.io/redux-in-chinese/docs/react-redux/api.html)

相關文章
相關標籤/搜索