redux阎毅、react-redux焚刚、redux-thunk

一個用于理解原理的簡寫版

redux

//redux有一下三個主要函數(shù)
//createStore
//getState  獲取當(dāng)前state
//dispatch  觸發(fā)state的更新


// 這個reducer是項目中state最終改變的地方
// 這里的enhancer是調(diào)用中間件的時候返回給我們的函數(shù),即applyMiddleWare(thunk)的返回值
export function createStore(reducer, enhancer) {
  // 如果存在中間件的返回值扇调,就觸發(fā)creaestore矿咕,得到他返回的store。然后對dispatch做修改狼钮,最終作為新的store返回給provider
  if(enhancer){
    return enhancer(createStore)(reducer)
  }
  let currentState = {};
  // currentListener 是訂閱事件存放的數(shù)組碳柱,每當(dāng)dispatch的時候,都出遍歷這個數(shù)組去觸發(fā)事件
  let currentListener = [];

  function getState() {
      return currentState;
  }

  // 監(jiān)聽的事件從這里進(jìn)入
  function subscribe(listener) {
      currentListener.push(listener);
  }

  // 觸發(fā)state的改變熬芜,觸發(fā)監(jiān)聽的事件
  function dispatch(action) {
      // 觸發(fā)state的改變莲镣,state代表著整個app的狀態(tài),這個狀態(tài)是由我們維護(hù)的涎拉,是我們自己描述的
      currentState = reducer(currentState,action);
      // 遍歷監(jiān)聽數(shù)組中的所有監(jiān)聽事件
      currentListener.forEach(v=>v());
      return action;
  }
  // 在第一次createstore的時候瑞侮,currentState是應(yīng)當(dāng)有值的的圆,所以我們手動觸發(fā)第一次的dispatch,
  // 因為state只能通過dispatch改變半火,這里傳遞一個非常特殊的type類型越妈,用來避開用戶編寫reducer時所設(shè)置的type
  dispatch({type:'@@demoredux'});

  return { getState, subscribe, dispatch }
}

export function applyMiddleWare(...middlewares) {
  return createStore=>(...args)=>{
    const store = createStore(...args);
    let dispatch = store.dispatch;

    const midApi = {
      getState: store.getState,
      dispatch: (...args)=>dispatch(...args)
    };
    // 所有操作的目標(biāo)都是state,而只有dispatch才可以改變state钮糖,所以梅掠,中間件就是對dispatch進(jìn)行一層封裝,以實現(xiàn)他想要達(dá)到的功能
    // dispatch = middleware(midApi)(store.dispatch);
    // 返回一個數(shù)組藐鹤,這個數(shù)組是由準(zhǔn)備接受next參數(shù)的函數(shù)構(gòu)成的
    const middlewareChain = middlewares.map(middleware=>middleware(midApi));
    dispatch = compose(...middlewareChain)(store.dispatch);
    return {
      ...store,
      dispatch
    }

  }
}

function compose(...funcs) {
  if(funcs.length===0){
    return arg=>arg
  }
  if(funcs.length===1){
    return funcs[0]
  }
  // 獲取最后一個函數(shù)
  const last = funcs[funcs.length - 1];
  // 獲取除最后一個以外的函數(shù)[0,length-1)
  const rest = funcs.slice(0, -1);
  // 通過函數(shù) curry 化
  return (...args) => rest.reduceRight((composed, f) => f(composed), last(...args))
  // 新版redux-thunk中使用以下方式 reduce省略第二個參數(shù)的時候瓤檐,第二個參數(shù)的默認(rèn)值為數(shù)組中的第一個元素
  // return funcs.reduce((a, b) => (...args) => a(b(...args)))
}

// 從createStore返回的對象來看,state是不向外暴露的娱节,只通過store向外暴露的三個方法挠蛉,去獲取,監(jiān)聽以及觸發(fā)


function bindActionCreator(creator, dispatch) {
  return (...args)=>dispatch(creator(...args));
}
// 這個工具函數(shù)用來處理action肄满,因為單獨執(zhí)行action是沒有任何效果的谴古,我們需要將所做動作的類型與改變內(nèi)容傳遞給dispatch才可以
export function bindActionCreators(creators, dispatch){
  // 將包裹的結(jié)果放在bound里
  let bound = {};
  Object.keys(creators).forEach(v=>{
    let creator = creators[v];
    // 我思考過這里為什么不用箭頭函數(shù)去實現(xiàn),因為這里需要的是一個自執(zhí)行函數(shù)稠歉,用來給返回的函數(shù)提供一個作用域去存放dispatch和creator
    // 如果寫成剪頭函數(shù)掰担,無法自執(zhí)行
    bound[v] = bindActionCreator(creator, dispatch);
  });
  return bound;

  // 可以用reduce進(jìn)行累加操作,更加的函數(shù)式
  // return Object.keys(creators).reduce((res, item)=>{
  //   res[item] = bindActionCreator(creators[item], dispatch)
  // },{});
}

react-redux

// context  PropTypes,使用context怒炸,必須先定義PropTypes
// 首先在父組件內(nèi)定義
// static childContextTypes = {}
// getChildContext(){
//     return 想要放入context內(nèi)部的東西
// }
// 然后带饱,在子組件內(nèi)部,首先定義proptypes
// static contextTypes = {}
// 使用this.context.*來獲取想要的東西

// 這個context阅羹,就是provider里存放store的地方

import React from 'react';
import PropTypes from 'prop-types';
import bindActionCreators from './redux';

class Provider extends React.Component{
  static childContextTypes = {
    store: PropTypes.object
  };

  //不清楚這里為什么傳入context
  constructor(props, context){
    super(props, context);
    //因為store是我們調(diào)用createStore后勺疼,以props傳遞給provider的,所以在這里這樣獲取
    this.store = props.store;
  }

  getChildContext(){
      return { store: this.store };
  }

  render(){
      return this.props.children;
  }
}

// connect

const connect = (mapStateToProps=state=>state, mapDispatchToProps={})=>(Wrapcomponent)=>(
  class ConnectComponent extends React.Component{
    // 每一個組件都要獲取我們寫在context里的store
    static contextTypes = {
      store: PropTypes.object
    };
    constructor(props, context){
      super(props, context);
      this.state = {
        props: {}
      }
    }

    componentDidMount(){
      // 不理解這里為什么要訂閱update捏鱼,我覺得mapStateToProps和mapDispatchToProps在寫定之后并不會改變
      // 更新是為了update里的setState方法执庐,確保組件在dispatch之后可以更新,可是這樣子不就相當(dāng)于只要有更改
      // 頁面所有組件都會強(qiáng)制刷新导梆,會有性能問題嗎
      const { store } = this.context;
      store.subscribe(()=>this.update());
      this.update();
    }

    update(){
      //獲取mapStateToProps和mapDispatchToProps 放入this.props里
      const { store } = this.context;
      //mapStateToProps和mapDispatchToprops都是由我們寫的轨淌,我們要告訴子組件,傳遞給他什么
      const stateProps = mapStateToProps(store.getState());
      const dispatchProps = bindActionCreators(mapDispatchToProps);
      this.setState({
        props: {
          ...this.state.props,
          ...stateProps,
          ...dispatchProps
        }
      })
    }


    render(){
      return <Wrapcomponent {this.state.props}/>
    };


  }
);

export default { Provider }

thunk

const thunk = ({dispatch,getState})=>(next)=>(action)=>{
    if(typeof action === 'function' ){
        return action(dispatch, getState)
    }
    return next(action)
};

export default thunk
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末看尼,一起剝皮案震驚了整個濱河市递鹉,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌藏斩,老刑警劉巖梳虽,帶你破解...
    沈念sama閱讀 222,464評論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異灾茁,居然都是意外死亡窜觉,警方通過查閱死者的電腦和手機(jī)谷炸,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,033評論 3 399
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來禀挫,“玉大人旬陡,你說我怎么就攤上這事∮镉ぃ” “怎么了描孟?”我有些...
    開封第一講書人閱讀 169,078評論 0 362
  • 文/不壞的土叔 我叫張陵,是天一觀的道長砰左。 經(jīng)常有香客問我匿醒,道長,這世上最難降的妖魔是什么缠导? 我笑而不...
    開封第一講書人閱讀 59,979評論 1 299
  • 正文 為了忘掉前任廉羔,我火速辦了婚禮,結(jié)果婚禮上僻造,老公的妹妹穿的比我還像新娘憋他。我一直安慰自己,他們只是感情好髓削,可當(dāng)我...
    茶點故事閱讀 69,001評論 6 398
  • 文/花漫 我一把揭開白布竹挡。 她就那樣靜靜地躺著,像睡著了一般立膛。 火紅的嫁衣襯著肌膚如雪揪罕。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,584評論 1 312
  • 那天宝泵,我揣著相機(jī)與錄音好啰,去河邊找鬼。 笑死鲁猩,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的罢坝。 我是一名探鬼主播廓握,決...
    沈念sama閱讀 41,085評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼嘁酿!你這毒婦竟也來了隙券?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 40,023評論 0 277
  • 序言:老撾萬榮一對情侶失蹤闹司,失蹤者是張志新(化名)和其女友劉穎娱仔,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體游桩,經(jīng)...
    沈念sama閱讀 46,555評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡牲迫,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,626評論 3 342
  • 正文 我和宋清朗相戀三年耐朴,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片盹憎。...
    茶點故事閱讀 40,769評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡筛峭,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出陪每,到底是詐尸還是另有隱情影晓,我是刑警寧澤,帶...
    沈念sama閱讀 36,439評論 5 351
  • 正文 年R本政府宣布檩禾,位于F島的核電站挂签,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏盼产。R本人自食惡果不足惜饵婆,卻給世界環(huán)境...
    茶點故事閱讀 42,115評論 3 335
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望辆飘。 院中可真熱鬧啦辐,春花似錦、人聲如沸蜈项。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,601評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽紧卒。三九已至侥衬,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間跑芳,已是汗流浹背轴总。 一陣腳步聲響...
    開封第一講書人閱讀 33,702評論 1 274
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留博个,地道東北人怀樟。 一個月前我還...
    沈念sama閱讀 49,191評論 3 378
  • 正文 我出身青樓,卻偏偏與公主長得像盆佣,于是被迫代替她去往敵國和親往堡。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,781評論 2 361

推薦閱讀更多精彩內(nèi)容