手寫 redux 和 react-redux
阿新 • • 發佈:2018-06-17
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