Redux教程3:添加倒计时

前面的教程里面,我们搭建了一个简单红绿灯示例,通过在console输出当面的倒计时时间;由于界面上不能显示倒计时,用户体验并不良好,本节我们就添加一个简单的倒计时改善一下。

作为本系列的最后一篇文章,将示例如何处理多个Redux、React的情形;

1、创建Counter类

我们定义倒计时的类名为 Counter ,创建所需要的文件(夹):

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;

可以通过 http://localhost:3000/counter/redux 检验是否正常运行;


(这个是gif图,如果没动画请点击在新窗口打开)

在假设用户已经编写上面的代码文件的基础上,我们继续讲解如何将 Counter 和 Light 两个组件联合起来。

2、创建入口文件

Redux的三个原则之一 : 单一store,单一reducer。我们创建两个文件,分别整合之前所写的 reducer 和 store 。

2.1、reducer入口文件

创建 reducers/traffic.js文件,作为 主reducer 入口文件:

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.2、store入口文件

创建 stores/traffic.js文件,作为 主store 入口文件:

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

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

可以看到并没有什么工作量,只是多了几行代码而已;

3、创建应用

前面创建的 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后查看;

3.1、初始化

在 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);
  • 初始化的时候,我们从绿灯开始;
  • 倒计时的时间来自于 initLight.light.time ,这样在初始化状态的时候关联起来两个组件
  • 将两个组件的状态(initLight,initCount)合并成 initState ,传给应用的 store,以完成 应用store的初始化

3.2、创建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中返回的对象有两个属性 light 和 count,在React组件中 对应 this.props.light 、 this.props.count
  • mapDispatchToProps 中现将两个组件的方法先和dispatch绑定,合成一个对象之后再赋值,这样在React组件中使用 this.props.actions 可以调用这两个组件的所有的actions创造函数;
  • 最后使用 <Provider> 注入 store 实例;

3.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.4、预览效果

在 http://localhost:3000/app/traffic 中查看效果,效果正如此系列文章第一篇开头所展示的那样,红绿灯搭配倒计时运行:


(这个是gif图,如果没动画请点击在新窗口打开)

红绿灯初始状态是 绿灯5s,继而循环 黄灯3s -> 红灯7s -> 绿灯5s -> 黄灯3s -> ...

就这样, Counter 和 Light 融洽地结合起来,完美,happy ending~

4、总结

到这里,Redux 的入门教程算是完结;整个过程下来,你可以体会得到,React只需要关注逐渐的展示就行了,所有状态的管理交由 redux 即可,这种绑定恰好体现了 容器组件和展示组件相分离 的开发思想: 只在最顶层组件(如路由操作)里使用 Redux;内部组件应该像木偶一样保持“呆滞”,所有数据都通过 props 传入 。

这里需要再强调一下:Redux 和 React 之间没有关系。Redux 支持 React、Angular、Ember、jQuery 甚至纯 JavaScript。

此系列文章仅仅是个开始,后续Redux还有许多高阶内容需要学习,比如异步数据流等处理。少年,加油吧~

时间: 2024-09-19 09:19:38

Redux教程3:添加倒计时的相关文章

Redux教程1:环境搭建,初写Redux

[序] 如果将React比喻成士兵的话,你的程序还需要一位将军,去管理士兵(的状态),而Redux恰好是一位好将军,简单高效: 相比起React的学习曲线,Redux的稍微平坦一些:本系列教程,将以"红绿灯"为示例贯穿整个demo,希望能让用户快速理解&学习Redux: 强烈推荐 Redux 中文文档,本redux教程所有的材料和思路都来源于此: 这个系列拆分成3篇文章,最后获得的效果图为: (这个是gif图,如果没动画请点击在新窗口打开) 红绿灯初始状态是 绿灯5s,继而循环

Excel2007教程:添加一张新工作表到工作簿

Excel2007基础教程:添加一张新工作表到工作簿 工作表可以成为一个非常好的组织工具.您可以根据逻辑需要把不同的元素放在不同的工作表中,而不是把所有的东西全都放在同一张工作表中.例如,如果 您有几种产品,对它们的销售单独进行记录,那么就可以把每一样产品记录放在各自的工作表中,然后再使用其他工作表合并这些结果. 下面是三种向工作簿添加工作表的方法: 单击位于最后一张表标签右侧的"插入工作表"控件.该方法在工作簿的最后一张表之后插入一张新表. 按Shift+Fll 键.该方法在活动表之

java-mysql 搭建时,看到教程 没有添加 Class.forName(``);

问题描述 mysql 搭建时,看到教程 没有添加 Class.forName(``); 教程里没有添加这句Class.forName("com.mysql.jdbc.Driver");是正常的,而我copy的代码,没有这句却报异常 求助,是怎么个原因啊. 505050505050505050505050505050505050 解决方案 你这个教程是什么教程?检查以下两点: 1.mysql的driver类是不是在别的地方加载过了?这个不一定要卸载getconnection里面,只要保证

ASP.NET MVC 5 学习教程:添加验证

原文 ASP.NET MVC 5 学习教程:添加验证 起飞网 ASP.NET MVC 5 学习教程目录: 添加控制器 添加视图 修改视图和布局页 控制器传递数据给视图 添加模型 创建连接字符串 通过控制器访问模型的数据 生成的代码详解 使用 SQL Server LocalDB Edit方法和Edit视图详解 添加查询 Entity Framework 数据迁移之添加字段 添加验证 Details 和 Delete 方法详解 在本节中,我们将为Movie模型添加验证逻辑,并确认验证规则在用户试图

ASP.NET MVC 5 学习教程:添加视图

原文 ASP.NET MVC 5 学习教程:添加视图 起飞网 ASP.NET MVC 5 学习教程目录: 添加控制器 添加视图 修改视图和布局页 控制器传递数据给视图 添加模型 创建连接字符串 通过控制器访问模型的数据 生成的代码详解 使用 SQL Server LocalDB Edit方法和Edit视图详解 添加查询 Entity Framework 数据迁移之添加字段 添加验证 Details 和 Delete 方法详解 在本节内容中,我们将修改HelloWorldController类,使

ASP.NET MVC 5 学习教程:添加控制器

原文 ASP.NET MVC 5 学习教程:添加控制器 起飞网 ASP.NET MVC 5 学习教程目录: 添加控制器 添加视图 修改视图和布局页 控制器传递数据给视图 添加模型 创建连接字符串 通过控制器访问模型的数据 生成的代码详解 使用 SQL Server LocalDB Edit方法和Edit视图详解 添加查询 Entity Framework 数据迁移之添加字段 添加验证 Details 和 Delete 方法详解 MVC代表了模型-视图-控制器.MVC是一个架构良好,可测试性和易于

ASP.NET MVC 5 学习教程:添加查询

原文 ASP.NET MVC 5 学习教程:添加查询 起飞网 ASP.NET MVC 5 学习教程目录: 添加控制器 添加视图 修改视图和布局页 控制器传递数据给视图 添加模型 创建连接字符串 通过控制器访问模型的数据 生成的代码详解 使用 SQL Server LocalDB Edit方法和Edit视图详解 添加查询 Entity Framework 数据迁移之添加字段 添加验证 Details 和 Delete 方法详解 在本节中,我们为 Index 方法添加查询功能,使我们能够根据电影的题

ASP.NET MVC 5 学习教程:添加模型

原文 ASP.NET MVC 5 学习教程:添加模型 起飞网 ASP.NET MVC 5 学习教程目录: 添加控制器 添加视图 修改视图和布局页 控制器传递数据给视图 添加模型 创建连接字符串 通过控制器访问模型的数据 生成的代码详解 使用 SQL Server LocalDB Edit方法和Edit视图详解 添加查询 Entity Framework 数据迁移之添加字段 添加验证 Details 和 Delete 方法详解 在本节中,我们将添加一些管理电影数据库的类,这些类在ASP.NET M

Redux教程2:链接React

序 通过前面的教程,我们有了简单的环境,并且可以运行 Redux 的程序,也对 如何编写Redux示例 有了初步的印象: 掌握了 使用Redux控制状态转移,继而驱动 React 组件发生改变,这才是学习Redux的初衷. 本篇我们将 Redux 和 React 联合起来,着重讲解 redux-react 模块的使用: 1.编写红绿灯React组件 在原有的基础上,我们编写红绿灯组件: touch components/light/index.js components/light/index.