1. 程式人生 > >手寫 redux 和 react-redux

手寫 redux 和 react-redux

create reat context 結果 生命周期 connect 組件 ren DC

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