redux是一個針對JavaScript應用的可預測的狀態管理器。javascript
定義:裝飾者模式用於給對象動態地增長職責。java
咱們來看看redux最先期(v0.2.0)的github代碼:react
//Counter.js import React from 'react'; import { performs, observes } from 'redux'; @performs('increment', 'decrement','double') @observes('CounterStore') export default class Counter { render() { const { increment, decrement } = this.props; return ( <p> Clicked: {this.props.counter} times {' '} <button onClick={() => increment()}>+</button> {' '} <button onClick={() => decrement()}>-</button> {' '} <button onClick={() => double()}>double</button> </p> ); } }
通過observes的包裝後,react組件能夠訪問Redux store裏的couter數據;通過performs的包裝後,react組件能夠發起increment、decrement和double這3個Action。git
咱們來看看performs是怎麼包裝react組件的:github
//performs.js import React, { Component, PropTypes } from 'react'; import pick from 'lodash/object/pick'; import identity from 'lodash/utility/identity'; const contextTypes = { getActions: PropTypes.func.isRequired }; export default function performs(...actionKeys) { let mapActions = identity; return function (DecoratedComponent) { const wrappedDisplayName = DecoratedComponent.name; return class extends Component { static displayName = `ReduxPerforms(${wrappedDisplayName})`; static contextTypes = contextTypes; constructor(props, context) { super(props, context); this.updateActions(props); } updateActions(props) { this.actions = mapActions( pick(this.context.getActions(), actionKeys), props ); } render() { return ( <DecoratedComponent {...this.actions} /> ); } }; }; }
很簡單對不對,performs實質上是一個高階函數,接收一個react組件類型的參數DecoratedComponent,而後返回一個高階組件,該組件包裝了傳遞進來的react組件,並向該組件傳遞了action相關的props.redux
經過能夠看上面的圖能夠看出,Counter組件被Observes包裝後,又被performs包裝,造成了一條包裝鏈。設計模式
redux提供的API中,有一個重要的方法connect,用於鏈接 React 組件與 Redux store。鏈接操做不會改變原來的組件類,而是返回一個新的已與 Redux store 鏈接的組件類。典型的裝飾者模式有木有?app
定義:觀察者模式又叫發佈-訂閱模式,它定義對象間的一種一對多的依賴關係,當一個對象的狀態發生改變時,全部依賴它的對象都將獲得通知。ide
@observes('CounterStore')
counter.js的這行代碼表示它對Redux的CounterStore數據進行訂閱。咱們來看看objserves的實現:函數
//observes.js import React, { Component, PropTypes } from 'react'; import pick from 'lodash/object/pick'; const contextTypes = { observeStores: PropTypes.func.isRequired }; export default function connect(...storeKeys) { return function (DecoratedComponent) { const wrappedDisplayName = DecoratedComponent.name; return class extends Component { static displayName = `ReduxObserves(${wrappedDisplayName})`; static contextTypes = contextTypes; constructor(props, context) { super(props, context); this.handleChange = this.handleChange.bind(this); this.unobserve = this.context.observeStores(storeKeys , this.handleChange); //訂閱store數據 } handleChange(stateFromStores) { this.currentStateFromStores = pick(stateFromStores, storeKeys); this.updateState(stateFromStores); } updateState(stateFromStores, props) { stateFromStores = stateFromStores[storeKeys[0]]; const state = stateFromStores; this.setState(state);//經過setState進行組件更新 } componentWillUnmount() { this.unobserve();//退訂 } render() { return ( <DecoratedComponent {...this.props} {...this.state} /> ); } }; }; }
當數據變化時,經過調用setState方法,進而對Counter組件進行UI更新。