手写 redux 和 react-redux

Posted crazycode2

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了手写 redux 和 react-redux相关的知识,希望对你有一定的参考价值。

1.手写 redux

redux.js

/**
 * 手写 redux
 */
export function createStore(reducer) {
  // 当前状态
  let currentState;
  // 监听队列
  let currentListeners = [];
  // 订阅(观察)
  function subscribe(listener) {
    // 将listener放入数组
    currentListeners.push(listener);
    // return 一个方法 取消注册
    return function() {
      currentListeners = currentListeners.filter(function(l) {
        return l !== listener;
      })
    }
  }
  // 分发
  function dispatch(action) { // 接收 action 对象
    /**
     * 处理完得到的结果,给当前的状态
     * 执行外部传入的 render 方法
     */
    currentState = reducer(currentState, action);
    /**
     * 将订阅者方法 挨个执行一次
     */
    currentListeners.forEach((v) => v())
  }
  // 获取状态值
  function getState() {
    // 返回当前值
    return currentState;
  }

  // 返回的是个 store 对象
  return {subscribe, dispatch, getState}
}

/**
 * 用于 react-redux 调用
 */
export function bindActionCreator(creator, dispatch) {
  // 包装一下
  return (...args) => {dispatch(creator(...args))}
}

export function bindActionCreators(creators, dispatch) {
  let bound = {};
  Object.keys(creators).forEach(v => {
    let creator = creators[v];
    bound[v] = bindActionCreator(creator, dispatch);
  })

  return bound;
}

2.手写 react-redux

myReactRedux.js

/**
 * 手写 react-redux
 * Provider -> context 技术。可以跨级来使用属性。
 */
import React, { Component } from ‘react‘;
// 类型检查
import PropTypes from ‘prop-types‘;
// 引入手写的redux
import { bindActionCreators } from ‘./redux‘;

// connect 方法 -- 用于获取数据
export function connect(mapStateToProps=state=>state, mapDispatchToProps={}) { // 高阶组件(方法) -- 去状态的组件
  /**
   * mapStateToProps 把 状态 转成 属性
   * mapDispatchToProps 把 action方法 转成 属性
   * react 性能优化最主要的一点是减少状态的使用,将值转为this.props的属性值来代替
   */
  // 柯里化
  return function(OldComponent) {
    return class NewComponent extends Component {
      static contextTypes = {
        store: PropTypes.object // 类型与外部相同
      }
      // 构造函数
      constructor(props, context) {
        super(props, context);
      
        this.state = { // 定义内部状态
          props: {
            ...props // 拆分符
          }
        };
      }

      // 生命周期 -- 首次加载之后调用
      componentDidMount(){
        // 添加监听
        const store = this.context.store;
        store.subscribe(() => this.update());
        this.update();
      }

      update(){
        const store = this.context.store;
        const stateProps = mapStateToProps(store.getState()); // 把store中的数据,变为react组件的props属性
        // 把原有的 action 加工, 返回 dispatch(action)
        const dispatchProps = bindActionCreators(mapDispatchToProps, store.dispatch);
        this.setState({
          props: { // 将传入的对象的值作为props的属性
            ...this.state.props,
            ...stateProps,
            ...dispatchProps
          } // 类型于对象合并
        })
      }

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

// /**
//  * 上述代码 es6 写法
//  * 函数柯里化
//  */
// export const connect = (mapStateToProps, mapDispatchToProps) => (OldComponent) => {
//   // 接收旧的组件,返回新的组件
//   return class NewComponent extends Component {
//     //...
//   }
// }

// 创建 Provider 组件(类) -- 使得任何内容都可以直接在组件内通过 connect 使用 store
export class Provider extends Component {
  // 构造函数
  constructor(props, context) { // context 上下文
    super(props, context);
  
    this.store = props.store; // 得到传入的store属性
  }
  /**
   * 定义静态对象
   * 类型校验 -- 确定是由哪个组件提供的store
   */
  static childContextTypes = {
    store: PropTypes.object
  };
  // 获取 child 上下文
  getChildContext(){
    // 向外提供,便于Provider所有子组件调用
    return {store: this.store}
  }
  // 直接渲染Provider的子组件
  render() {
    return this.props.children;
  }
}

3.测试

demo.js

/**
 * 引入 redux
 * reducer 和 action 会写在一个文件中
 */
// import { createStore } from ‘redux‘;
import { createStore } from  ‘./redux‘;

// reducer
function inputChange(state = 10, action) {
  switch(action.type){
    case ‘add‘: // 加
      return state + 1;
    case ‘sub‘: // 减
      return state - 1;
    default: // 默认
      return state;
  }
}

// 定义store
const store = createStore(inputChange);

// 通过 subscribe 订阅数据流
store.subscribe(listener); // 23中设计模式 -- 观察者模式

function listener(argument) {
  const current = store.getState();
  console.log(`当前数据:${current}`);
}

console.log(store.getState()); // 10

store.dispatch({type: ‘add‘}); // 11
store.dispatch({type: ‘add‘}); // 12
store.dispatch({type: ‘sub‘}); // 11


/**
 * 隐式转换 -- 优先级 -- 转换顺序
 * Object 高级数据结构 可以转换成 String/Boolean/Number 初级数据结构
 */
console.log({}.length); // undefined
console.log(({}+{}).length); // 30 -- string 优先级高于 number 所以变成字符串相加 -- [object Object] [object Object] 30

.

以上是关于手写 redux 和 react-redux的主要内容,如果未能解决你的问题,请参考以下文章

手写一个React-Redux,玩转React的Context API

将 props 传递给 react-redux 容器组件

React-Redux的使用

redux和react-redux

P19:Redux进阶-React-Redux介绍和安装

redux超易学三篇之二(开始使用react-redux)