从实现计数器说起

redux流程

1.基本计数器

功能需求:点击+按钮使得计数加一,点击-按钮使得计数减一,实时显示计数值。功能分析:根据功能,所以我们可以分析出只需要一个state,这个state代表着计数值。很简单(性能优化方面暂时搁下),直接上代码。

class Counter extends Component{
    constructor(props){
        super(props)
        this.incrementClick = this.incrementClick.bind(this)
        this.decrementClick = this.decrementClick.bind(this)
        this.state = {
            "times": 0
        }
    }

    incrementClick(ev){
        this.setState({"times": this.state.times+1})
    }

    decrementClick(ev){
        let value = (this.state.times-1) >= 0 ? (this.state.times-1) : 0;
        this.setState({"times": value})
    }

    render(){
        return (
            <div>
                <button style={buttonStyle} onClick={this.incrementClick}>+</button>
                <span style={contentStyle}>{this.state.times}</span>
                <button style={buttonStyle} onClick={this.decrementClick}>-</button>
            </div>
        )
    }
}

项目地址

效果展示


2.基本计数器组

现在实现另一个功能,那就是实现计数器组,有多个计数器,每个计数器可以增加也可以减少,并且实时显示计数值。而且需要实时显示所有得计数值之和。功能分析:有多少个计数器,应该就需要有多少个state,下面可以看看具体实现:

class Counter extends Component{
    constructor(props){
        super(props)
        this.state = {
            "first": 1,
            "second": 2,
            "third": 3
        }
    }

    handleClick(obj){
        console.log(obj)
        return function(){
            this.setState(obj)
        }.bind(this)//位置1
    }

    render(){
        let summary = 0;
        //注意一定要不能省略变量定义关键字,否则会出错,大概react的源代码开启了严格模式
        for(var key in this.state){
            if(this.state.hasOwnProperty(key)){
                summary += this.state[key]
            }
        }
        
        return (
            <div>
                <div style={divStyle}>
                    <button style={btnStyle} onClick={this.handleClick({"first": this.state.first+1})()}>+</button>
                    <span style={spanStyle}>{this.state.first}</span>
                    <button style={btnStyle} onClick={this.handleClick({"first": (this.state.first-1)>=0 ? (this.state.first-1): 0})()}>-</button>
                </div>

                <div style={divStyle}>
                    <button style={btnStyle} onClick={this.handleClick({"second": this.state.second+1})()}>+</button>
                    <span style={spanStyle}>{this.state.second}</span>
                    <button style={btnStyle} onClick={this.handleClick({"second": (this.state.second-1)>=0 ? (this.state.second-1) : 0})()}>-</button>
                </div>

                <div style={divStyle}>
                    <button style={btnStyle} onClick={this.handleClick({"third": this.state.third+1})()}>+</button>
                    <span style={spanStyle}>{this.state.third}</span>
                    <button style={btnStyle} onClick={this.handleClick({"third": (this.state.third-1)>=0 ? (this.state.third-1) : 0})()}>-</button>
                </div>

                <div style={sumStyle}>
                    <span style={spanStyle}>{summary}</span>
                </div>
            </div>
        )
    }
}

项目地址

效果展示

总结:需要注意的地方,我们不能再react组件中使用for(key in obj),必须得利用变量定义符声明这个变量,原因是ES2015引入的class语法规定了在class body里面使用严格模式("use strict")。从这里应该学习到:尽管在JavaScript中for语句块并不是一个块作用域,但是仍旧不要养成了不使用变量声明标识符得习惯。同时,这个项目还涉及到了JavaScript中一个很重要得知识点:环境对象,在位置一处,我们必须bind(this),否则返回的这个函数的环境对象将不会是react组件对象,但是我们的合成事件handleClick就无需绑定环境对象为react组件对象,因为在handleClick函数内部并没有使用任何与react component相关逻辑,所以无需绑定。

由于需要要计算计数值的总和,所以我们在这里实现的基本计数器组将所有的逻辑都挤在一个组件中,但是这样做的缺点也很明显,这样的组件太过杂乱,同时也不利于复用。那么问题来了,有更好的方法吗?别的方法我也不知道,下面我们来直接试试利用redux库吧。。


3.利用redux库构建计数器组

由于代码比较多,所以这里就不贴代码了。

项目地址

效果展示

关于使用redux结合react来构建应用所需要注意的地方是:明白redux库的核心概念:数据单向流动,纯函数。对于每个文件,redux都提供了范式的,比如说对于actionTypes文件,我们所需要做的就是定义应用的交互事件;对于actions文件来说,我们需要做的就是根据每个交互事件来定义相应的actioncreater函数,这个函数的参数是自定义的,在日后dispatch的时候传入即可,actioncreater函数返回一个对象,这个对象必须包含type字段以供reducer函数识别交互动作的类型,当然这个对象也可以包括其他数据,以协助reducer函数迭代store所维护的状态。对于reducer文件来说,它的格式更是固定的,reducer函数接受两个参数,第一个参数是previousState,第二个参数是actionCreater所返回的交互动作对象。reducer函数所完成的逻辑就是根据action的类型来迭代store中的相关状态,要注意的是,我们的reducer函数是一个纯函数。至于store文件,如果我们的整个应用就只有一个reducer函数的话,那么就无需使用combineReducer函数将众多reducer组合成一个高阶函数,那么此时的核心就是createStore函数了,这个函数的第一个参数是reducer函数,第二个参数是默认的初始化state,如果缺省这个参数的话,那么在reducer函数的previousState参数应该会有默认值。

关于组件的组织,我们习惯利用容器组件来与store进行交互,他负责从store取得所需要的相应初始状态,负责更改store所维护的状态,负责让组件所需要的数据与store所维护的相应状态保持同步变化。具体细节就是分别是:store.getState;在事件处理函数中内部调用store.dispatch(actionCreater(此时传入参数));在componentWillMount函数里面增添store.subscribe()来订阅store所维护得状态得变化,一旦发生变化,那么将会调用subscribe的callback,这个calback应该完成的逻辑就是调用setState,重新从store中取得所需要的state。

我们的容器组件负责与store进行交互,对于视图组件来说,它的功能就是展示数据了。而且容器组件和视图组件是父子组件的关系,视图组件所需要展示的数据均是通过父组件所传入进来的props实现的,因此,展示组件一般没有必要具有state,所以一般的我们一般都将展示组件利用无状态函数来实现,这样做同时也可以提高性能。


4.进一步使用react-redux进行抽象

项目地址

效果图

在react项目中使用redux的话,那么顺便使用react-redux将会是一个非常不错的选择,因为react-redux包提供了许多便捷的方法,同时它们也带来了抽象感。使用react-redux包的话,那么我们不在需要花费大量的功夫为视图组件定义一个容器组件来与store交互了,利用react-redux提供的connect方法返回的函数,我们可以利用视图组件来生成一个容器组件。所以对于react-redux来说,核心就是理解它的connect方法部分,下面看看connect方法的函数签名:

connect(mapStateToProps, mapDispatchToProps) => function

在这里我们只需要理解一下mapStateToProps和mapDispatchToProps的用法,它们各自的函数签名如下:

mapStateToProps(storeState, ownProps)
mapDispatchToProps(dispatch, ownProps)

要想搞清上面两个方法的作用,首先我们需要先明白容器组件的功能:当用户做出响应时,容器组件需要dispatch一个action,具体做法就是dispatch(actionCreater(param)),为什么要dispatch动作?因为要迭代store嘛,dispatch了之后,redux的reducer就会根据action的类型对store所维护的state做出相应的更改;当store所维护的组件被更改后,我们的容器组件还得保持自己的数据和store里的状态保持一致,要如何实现呢?答案就是利用subscribe订阅修改事件,一旦状态发生改变就会调用传递给subscribe的callback,我们在这个callback里面所需要完成的逻辑就是从store中取出我们所需要的state,接着在setState即可。

理解了容器组件所完成的大致功能后,我们现在再回过头来看看mapStateToProps以及mapDispatchToProps,首先,这两个方法都是返回一个对象。前者方法所返回的对象应该包括我们的视图组件所需要渲染的数据,所以这个对象中应该具有视图组件所接受到的关于数据展示的props这个字段。理解一下它的参数,storeState参数即是store.getState()所返回的对象,ownProps即我们这个容器组件所接收到的来自父组件的内容。所以对于这个函数来说,他完成的功能就是保持视图组件所需要的数据与store所维护的状态保持同步。

再来看看mapDispatchToProps方法,他接受两个参数,第一个参数就是dispatch,他就是store.dispatch,ownProps就是容器组件所接受到的来自父组件的数据。他也是返回一个对象,这个对象所包括的内容就是我们在容器组件所自定义的合成事件,因此这个方法实现的就是迭代store中所维护的state。

END

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,547评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,399评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,428评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,599评论 1 274
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,612评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,577评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,941评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,603评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,852评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,605评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,693评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,375评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,955评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,936评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,172评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,970评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,414评论 2 342

推荐阅读更多精彩内容

  • 学习必备要点: 首先弄明白,Redux在使用React开发应用时,起到什么作用——状态集中管理 弄清楚Redux是...
    贺贺v5阅读 8,875评论 9 58
  • 做React需要会什么? react的功能其实很单一,主要负责渲染的功能,现有的框架,比如angular是一个大而...
    苍都阅读 14,737评论 1 139
  • 一、什么情况需要redux? 1、用户的使用方式复杂 2、不同身份的用户有不同的使用方式(比如普通用户和管...
    初晨的笔记阅读 2,013评论 0 11
  • 前言 本文 有配套视频,可以酌情观看。 文中内容因各人理解不同,可能会有所偏差,欢迎朋友们联系我讨论。 文中所有内...
    珍此良辰阅读 11,892评论 23 111
  • 一,预热 学术论文,尤其是知名学府的学术论文还是要有一定档次的,一定要大气,要有格局,通俗点说就是要有套路啦,不按...
    GemsLee阅读 493评论 0 6