zoukankan      html  css  js  c++  java
  • 手写 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

    .

  • 相关阅读:
    汇总遇到的问题
    mybatis下载地址
    快速创建数据大绝招
    常见的dos命令
    添加react-native-icons中文傻瓜式教程
    react native配置环境watchman监控安装失败解决办法
    如何使用SwitchyOmega.crx谷歌插件
    Git与Github学习笔记
    HTTP协议详解
    使用npm安装一些包失败了的解决方法(npm国内镜像介绍),安装速度跟cnpm一样哦
  • 原文地址:https://www.cnblogs.com/crazycode2/p/9193324.html
Copyright © 2011-2022 走看看