自定义redux库

654 阅读5分钟

自定义redux库

redux语法功能分析

  1. redux 库向外暴露下面几个函数
    • createStore(): 接收的参数为 reducer 函数, 返回为 store 对象
    • combineReducers(): 接收包含 n 个 reducer 方法的对象, 返回一个新的 reducer 函数
  2. store 对象的内部结构
    • getState(): 返回值为内部保存的 state 数据
    • dispatch(): 参数为 action 对象
    • subscribe(): 参数为监听内部 state 更新的回调函数

实现createStore()

实现createStore()的功能

/**
 * redux库主模块
 * 1. redux 库向外暴露下面几个函数
   - createStore(): 接收的参数为 reducer 函数, 返回为 store 对象
   - combineReducers(): 接收包含 n 个 reducer 方法的对象, 返回一个新的 reducer 函数
   2. store 对象的内部结构
   - getState(): 返回值为内部保存的 state 数据
   - dispatch(): 参数为 action 对象
   - subscribe(): 参数为监听内部 state 更新的回调函数
 */

/**
 * 
 * 根据指定的reducer函数创建一个store对象
 */
export function createStore(reducer) { //接收一个reducer函数

  //用来存储内部状态数据的变量,初始值为调用reducer函数返回的结果(外部指定的默认值)
  let state = reducer(undefined, { type: '@@redux/init' })
  //用来存储监听state更新回调函数的数组容器
  const listeners = []
  
  /**
   * 返回当前内部的state数据
   */
  function getState() {
    return state
  }
  /**
   * 分发action
   * 1.触发reducer调用,得到新的state
   * 2.保存新的state
   * 3.调用所有已存在的监视回调
   * 
   */
  function dispatch(action) {
    // 1.触发reducer调用,得到新的state
    const newState = reducer(state, action)
    // 2.保存新的state
    state = newState
    
    // 3.调用所有已存在的监视回调
    
    listeners.forEach(listener => listener())
  }
  /** 
   * 绑定内部state改变的监听回调
   * 可以给一个stort绑定多个监听
  */
  function subscribe(listener) {
    //保存到缓存Listener的容器数组中
    listeners.push(listener)
    console.log(listeners)
    console.log('触发subscribe')
  }
  //返回store对象
  return {
    getState,
    dispatch,
    subscribe
  }
}
/**
 * 
 * 整合传入参数对象中的多个reducer函数,返回一个新的reducer
 * 新的reducer管理的总状态:{r1:state1,r2:state2}
 */
export function combineReducers(reducers) {
  return (state, action) => {

  }
}

实现combineReducers

/**
 * 
 * 整合传入参数对象中的多个reducer函数,返回一个新的reducer
 * 新的reducer管理的总状态:{r1:state1,r2:state2}
 * reuscers的结构:
 * {
 *  count: (state=2,action) => 3
 *  user: (state = {}, action) => {}
 * }
 * 要得到的总状态的结构
 * {
 *  count:count(state.count,action),
 *  user:user(state.user,action)
 * }
 */
// const obj = {
//   count: (state = 2, action) => 3,
//   user: (state = {}, action) => { }
// }

/**
 * forEach版本
 */

export function combineReducers(reducers) {
  //返回一个新的总reducer函数
  //state:总状态
  return (state = {}, action) => {
    //准备一个总状态空对象
    const totalState = {}
    //执行reducers中每个reducer函数得到一个新的子状态,并添加到总状态空对象
    Object.keys(reducers).forEach(key => {
      totalState[key] = reducers[key](state[key], action)
    })
    return totalState
  }
}
/**
 * reduce版本
 */
// export function combineReducers(reducers) {
//   //返回一个新的总reducer函数
//   //state:总状态
//   return (state = {}, action) => {
//     //执行reducers中每个reducer函数得到一个新的子状态,并封装一个对象容器
//     const newState = Object.keys(reducers).reduce((preState, key) => {
//       preState[key] = reducers[key](state[key], action)
//       return preState
//     }, {})
//     return newState
//   }
// }

//简洁版本
export function combineReducers(reducers) {
  return (state = {}, action) => {
    return Object.keys(reducers).reduce((pre, key) => {
      pre[key] = reducers[key](state[key], action)
      return pre
    }, {})
  }
}

自定义redux-react

1.react-redux 向外暴露了 2 个 API
  • Provider 组件类

  • connect 函数

2.Provider 组件
  • 接收 store 属性

  • 让所有容器组件都可以看到 store, 从而通过 store 读取/更新状态

3.connect 函数

接收 2 个参数: mapStateToProps 和 mapDispatchToProps

mapStateToProps: 为一个函数, 用来指定向 UI 组件传递哪些一般属性

mapDispatchToProps: 为一个函数或对象, 用来指定向 UI 组件传递哪些函数属性

connect()执行的返回值为一个高阶组件: 包装 UI 组件, 返回一个新的容器组件

容器组件会向 UI 传入前面指定的一般/函数类型属性

代码

/**
 * react-redux库的主模块
 * 1) react-redux 向外暴露了 2 个 API a. Provider 组件类 b. connect 函数 
 * 2) Provider 组件 
 * 接收 store 属性 
 * 让所有容器组件都可以看到 store, 从而通过 store 读取/更新状态 
 * 3) connect 函数 
 * 接收 2 个参数: mapStateToProps 和 mapDispatchToProps 
 * mapStateToProps: 为一个函数, 用来指定向 UI 组件传递哪些一般属性 
 * mapDispatchToProps: 为一个函数或对象, 用来指定向 UI 组件传递哪些函数属性 
 * connect()执行的返回值为一个高阶组件: 包装 UI 组件, 返回一个新的容器组件 
 * 容器组件会向 UI 传入前面指定的一般/函数类型属性
 */
import React from 'react'
import PropTypes from 'prop-types'
/**
 * 用来向所有容器组件提供store的组件类
 * 通过context向所有的容器组件提供store
 */
export class Provider extends React.Component {

  static propTypes = {
    store: PropTypes.object.isRequired //声明接收store
  }
  /**
   * 声明提供context的数据名称和类型
   */
  static childContextTypes = {
    store: PropTypes.object
  }
  /**
   * 向所有有声明子组件提供包含要传递数据的context对象
   */
  getChildContext() {
    return { store: this.props.store }
  }
  render() {
    //返回渲染<Provider> 的所有子节点
    return this.props.children
  }
}

/**
 * connect高阶函数:接收 mapStateToProps 和 mapDispatchToProps ,返回一个高阶组件函数
 * 高阶组件:接收一个UI组件,返回一个容器组件
 */

export function connect(mapStateToProps, mapDispatchToProps) {
  //返回高阶组件函数
  return (UIComponent) => {
    //返回容器组件
    return class ContainerComponent extends React.Component {

      /**
       * 声明接收的context数据的类型
       */
      static contextTypes = {
        store: PropTypes.object
      }
      constructor(props, context) {
        super(props)
        console.log('ContainerComponent', context.store)
        //得到store
        const { store } = context
        //得到包含所有一般属性的对象
        const stateProps = mapStateToProps(store.getState()) //并且把state中的数据传递过去

        //将所有一般属性作为容器组件的状态数据
        this.state = { ...stateProps }

        //得到包含所有函数属性的对象
        let dispatchProps
        //判断传递过来的是否是函数
        if (typeof mapDispatchToProps === 'function') {
          //函数的情况
          dispatchProps = mapDispatchToProps(store.dispatch)//并且把dispatch传递过去
        } else { //对象的情况
          dispatchProps = Object.keys(mapDispatchToProps).reduce((pre, key) => {
            const actionCreator = mapDispatchToProps[key]
            pre[key] = (...args) => store.dispatch(actionCreator(...args)) //参数透传
            return pre
          }, {})
        }

        //保存到组件上
        this.dispatchProps = dispatchProps

        //绑定store的state变化的监听
        store.subscribe(() => {  // store内部的状态数据发生了变化
          console.log('11')
          //更新容器组件 ==>  UI更新
          this.setState(mapStateToProps(store.getState()))
        })
      }

      render() {
        //返回UI组件的标签
        return <UIComponent {...this.state} {...this.dispatchProps} />
      }
    }
  }
}

使用:

和react-redux使用方法一样