redux、react-redux、redux-thunk

一个用于理解原理的简写版

redux

//redux有一下三个主要函数
//createStore
//getState  获取当前state
//dispatch  触发state的更新


// 这个reducer是项目中state最终改变的地方
// 这里的enhancer是调用中间件的时候返回给我们的函数,即applyMiddleWare(thunk)的返回值
export function createStore(reducer, enhancer) {
  // 如果存在中间件的返回值,就触发creaestore,得到他返回的store。然后对dispatch做修改,最终作为新的store返回给provider
  if(enhancer){
    return enhancer(createStore)(reducer)
  }
  let currentState = {};
  // currentListener 是订阅事件存放的数组,每当dispatch的时候,都出遍历这个数组去触发事件
  let currentListener = [];

  function getState() {
      return currentState;
  }

  // 监听的事件从这里进入
  function subscribe(listener) {
      currentListener.push(listener);
  }

  // 触发state的改变,触发监听的事件
  function dispatch(action) {
      // 触发state的改变,state代表着整个app的状态,这个状态是由我们维护的,是我们自己描述的
      currentState = reducer(currentState,action);
      // 遍历监听数组中的所有监听事件
      currentListener.forEach(v=>v());
      return action;
  }
  // 在第一次createstore的时候,currentState是应当有值的,所以我们手动触发第一次的dispatch,
  // 因为state只能通过dispatch改变,这里传递一个非常特殊的type类型,用来避开用户编写reducer时所设置的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)
    };
    // 所有操作的目标都是state,而只有dispatch才可以改变state,所以,中间件就是对dispatch进行一层封装,以实现他想要达到的功能
    // dispatch = middleware(midApi)(store.dispatch);
    // 返回一个数组,这个数组是由准备接受next参数的函数构成的
    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]
  }
  // 获取最后一个函数
  const last = funcs[funcs.length - 1];
  // 获取除最后一个以外的函数[0,length-1)
  const rest = funcs.slice(0, -1);
  // 通过函数 curry 化
  return (...args) => rest.reduceRight((composed, f) => f(composed), last(...args))
  // 新版redux-thunk中使用以下方式 reduce省略第二个参数的时候,第二个参数的默认值为数组中的第一个元素
  // return funcs.reduce((a, b) => (...args) => a(b(...args)))
}

// 从createStore返回的对象来看,state是不向外暴露的,只通过store向外暴露的三个方法,去获取,监听以及触发


function bindActionCreator(creator, dispatch) {
  return (...args)=>dispatch(creator(...args));
}
// 这个工具函数用来处理action,因为单独执行action是没有任何效果的,我们需要将所做动作的类型与改变内容传递给dispatch才可以
export function bindActionCreators(creators, dispatch){
  // 将包裹的结果放在bound里
  let bound = {};
  Object.keys(creators).forEach(v=>{
    let creator = creators[v];
    // 我思考过这里为什么不用箭头函数去实现,因为这里需要的是一个自执行函数,用来给返回的函数提供一个作用域去存放dispatch和creator
    // 如果写成剪头函数,无法自执行
    bound[v] = bindActionCreator(creator, dispatch);
  });
  return bound;

  // 可以用reduce进行累加操作,更加的函数式
  // return Object.keys(creators).reduce((res, item)=>{
  //   res[item] = bindActionCreator(creators[item], dispatch)
  // },{});
}

react-redux

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

推荐阅读更多精彩内容