經過前面的教程,咱們有了簡單的環境,而且能夠運行Redux
的程序,也對 如何編寫Redux示例 有了初步的印象;css
掌握了 使用Redux控制狀態轉移 ,繼而驅動 React 組件發生改變,這纔是學習Redux的初衷。html
本篇咱們將 Redux 和 React 聯合起來,着重講解redux-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
有了React和以前的Redux,如今就要將二者連接起來了。咱們的目標是讓紅綠燈運行起來,就比如平時在十字路口看到的那樣;npm
再建立一個示例文件,就不叫demo了,叫作redux
好了:redux
touch components/light/redux.js
之因此示例文件名稱爲
demo.js
或redux.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();
繼而建立一個 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
方法中this
上下文,該方法來自文章Refactoring React Components to ES6 ClassessetTimeout
代替setInterval
實現;這是最爲關鍵的一個步驟,
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);
store
對象,真正store
對象的注入靠最後的<Provider store>
組件;(更多說明請參考 [react-redux 的 API][1]){light:state}
,這樣確保 Redux 中的 state 發生改變時,組件的 props.light 都是最新的 Redux state。dispatch
綁定到action
屬性,這樣在紅綠燈組件內讓其變成紅燈的時候,不須要dispatch(changeRed())
這麼調用,直接使用actions.changeRed()
,語義化更好;(更多說明請參考 [react-redux 的 API][1])<Provider store>
使組件層級中的 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代替;
理解了最爲困難的部分,以後的事情就水到渠成了;
如今,只要記住 在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 中查看。
在這個示例裏,經過點擊紅綠燈,每隔若干秒紅綠燈就會變換顏色,這說明二者已經連接起來;
(這個是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)