从Flux到Redux

单向数据流框架的始祖Flux

Flux理念的一个更强实现Redux

结合React和Redux

认识Flux

flux框架贯彻的最重要的观点 - 单向数据流

实际上Flux和React同时面世的。在2013年,Face-book公司让React亮相的同事,也推出了Flux框架。react和flux相辅相成。

做一个容易理解的对比,react是用来替代jquery的。那么flux就是替换backbone.js 、Ember.js等MVC框架为目的的。

对于MVC框架,为了让数据流可控,Controller应该是中心,当view要传递消息给Model时,应该调用Controller的方法,同样,当Model要更新view时,也应该通过controller引发新的渲染。


Flux的不足

  • 1.Store之间依赖关系
  • 2.难以进行服务器端渲染
  • 3.Store混杂了逻辑和状态

Redux的基本原则

redux

Flux的基本原则是“单向数据流”、Redux在此基础上强调三个基本原则

  • 唯一数据源
  • 保持状态只读
  • 数据改变只能通过纯函数完成

逐一理解三条基本原则

唯一数据源

唯一数据源指的是应用的状态数据应该只存储在唯一的一个Store上。从Flux来说,flux应用可以拥有多个Store、往往根据功能把应用的状态数据划分给诺干个Store分别存储管理。如果状态数据分散在多个Store中。容易造成数据冗余,这样数据一致性方面就会出现问题。

Redux对这个问题的解决方法就是,整个应用只保持一个Store、所有组件的数据源就是这个Store上的状态。

保持状态只读

保持状态只读,就是说不能去直接修改状态(setState),要修改Store的状态,必须要通过派发一个action对象完成。

UI=render(state) .(这个react公式)

我们已经说过驱动用户界面更改的是状态,如果状态都是只读的不能修改,怎么可能引起用户界面的变化呢?

当然,要驱动用户界面渲染,就要改变应用的状态,但是改变状态的方法不是去修改状态上值。而是创建一个新的状态对象返回给Redux。由Redux完成新的状态的组装。

这就直接引出了下面的第三个基本原则。

数据改变只能通过纯函数完成

这里所说的纯函数,就是Reducer,Redux名字的含义就是Reducer + Flux

Reducer不是一个Redux特定的术语,而是一个计算机科学中的通用概念,很多语言和框架都有对Reducer函数的支持。

我们以js为例。数据类型就有reduce函数,接受的参数就是一个reducer、reduce做的事情就是吧数组所有元素一次做“规约”,对每个元素都调用一次参数reducer、通过reducer函数完成规约所有元素的功能。

var array = [1,2,3,4]
function func(){
    array.reduce(function reducer(acc,item){
        console.log(acc+item);
        return acc+item;
    },0)
}
func();

上面的结果是 1 ,3, 6, 10

在Redux中,每个reducer的函数签名如下所示:

reducer(state,action)

第一个参数state是当前状态,第二个参数action是接受到的action对象。而reducer函数要做的事情,就是根据state 和 action的值产生一个新的对象返回,注意reducer必须是纯函数,也就是说函数的返回结果必须完全由参数state和action决定,而且不产生任何副作用,也不能修改参数state和action对象。

在redux中,一个实现reducer代码如下:

 function reducer( state, action) => { 
    const {counterCaption} = action; 
    switch (action. type) { 
            case ActionTypes. INCREMENT: 
        return {...state, [counterCaption]: state[ counterCaption] + 1}; 
            case ActionTypes. DECREMENT: 
        return {...state, [counterCaption]: state[ counterCaption] - 1}; 
            default: return state 
        } 
    }

可以看到reducer函数不光接受action为参数,还接受state未参数。也就是说,redux的reducer只负责计算状态,却并不负责存储状态。

我们可以看src/Actions.js文件中

export const increment = (counterCaption) => {
  return {
    type: ActionTypes.INCREMENT,
    counterCaption: counterCaption
  };
};

export const decrement = (counterCaption) => {
  return {
    type: ActionTypes.DECREMENT,
    counterCaption: counterCaption
  };
};

和Flux的src/Actions.js文件对比就会发现,Redux中每个action构造函数都返回一个action对象,而Flux版本中action构造函数,并不返回什么。而是把构造的动作函数立刻通过调用Dispatcher的dispatch函数派发出去

因为redux一般来说只有一个store。

import {createStore} from 'redux';
import reducer from './Reducer.js';

const initValues = {
  'First': 0,
  'Second': 10,
  'Third': 20
};

const store = createStore(reducer, initValues);

export default store;

在这里,我们接触到了Redux库提供的createStore函数。
这里第一个参数代表 reducer,第二个参数是状态的初始值,第三个参数可选,代表Store Enhancer。

确定好store状态,是设计好Redux应用的关键。

Redux的Store状态设计的一个主要原则:避免冗余

src/Reducer.js

import * as ActionTypes from './ActionTypes.js';

export default (state, action) => {
  const {counterCaption} = action;

  switch (action.type) {
    case ActionTypes.INCREMENT:
      return {...state, [counterCaption]: state[counterCaption] + 1};
    case ActionTypes.DECREMENT:
      return {...state, [counterCaption]: state[counterCaption] - 1};
    default:
      return state
  }
}

这里

return {...state, [counterCaption]: state[counterCaption] - 1};

其实,等同于下面的代码

const newState = Object.assign({},state);
newState[counterCaption]++;
return newState;

像上面这样写,创造了一个newState完全赋值了state、通过对newState的修改避免了对state的修改、不过这样写显得冗长、推荐使用扩展操作符,看起来更清晰简洁。

在reducer中绝对不能直接操作state

在View中的应用

接下来我们来看View部分

  • 首先我来看Counter.js

    在构造函数里,我们应该要注意

        this.state = this.getOwnState();
    

    而getOwnState()函数,是通过store.getState()这个API得来的。因此,在方法中。我们可以这样写

       getOwnState() {
          return {
            value: store.getState()[this.props.caption]
          };
        }
    

    在onChange函数中,我们不直接改变state,而是通过,改变,getOwnState()来改变响应的值。

       onChange() {
          this.setState(this.getOwnState());
       }
    

    在Counter中。我们可以看出ComponentDidMount中可以发现,subscribe监听其变化,只要Store的状态发生变化,就会调用这个组件的onChange的方法,在ComponentWillunmount中,我们可以将这个方法卸载掉。

        componentDidMount() {
           store.subscribe(this.onChange);
        }
      
        componentWillUnmount() {
          store.unsubscribe(this.onChange);
        }
    

    至于加和减的方法,我们不难可以看到。在redux中,action只负责创建一个对象,要派发action就需要调用store.dispatch函数。

    组件render函数所显示的内容,要么来自于props,要么来自于自身状态。

  • 其次我们来看Summary.js

    我们并没有状态来支持Summary组件,因为Summary组件的状态,完全可以通过把Counter状态数值加在一起得到,没有必须制造龙域数据存储。这也符合Redux唯一数据源的基本原则。

      getOwnState(){
          const state = store.getState();
          let sum = 0;
          for(let key in state){
              if(state.hasOwnProperty(key)){
                  sum+=state[key]
              }
          }
          return {sum : sum}
      }
    

    所以需要在getOwn-State状态中自己计算出所有计数值总和出来。

  • 最后我们看ControlPanel.js

    一个容器组件,装下Counter和Summary 就可以。

    class ControlPanel extends Component {
  render() {
    return (
      <div style={style}>
        <Counter caption="First" />
        <Counter caption="Second" />
        <Counter caption="Third" />
        <hr/>
        <Summary />
      </div>
    );
  }
}

参考文献

作者:程墨. 深入浅出React和Redux (实战)  北京华章图文信息有限公司. Kindle 版本. 

github地址......

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

推荐阅读更多精彩内容