前面的教程里面,咱们搭建了一个简单红绿灯示例,经过在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。