redux介绍
redux是一个针对JavaScript应用的可预测的状态管理器。
redux中的设计模式
装饰者模式
定义:装饰者模式用于给对象动态地增加职责。
我们来看看redux最早期(v0.2.0)的github代码:
//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。
我们来看看performs是怎么包装react组件的:
//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.
通过可以看上面的图可以看出,Counter组件被Observes包装后,又被performs包装,形成了一条包装链。
redux提供的API中,有一个重要的方法connect,用于连接 React 组件与 Redux store。连接操作不会改变原来的组件类,而是返回一个新的已与 Redux store 连接的组件类。典型的装饰者模式有木有?
观察者模式
定义:观察者模式又叫发布-订阅模式,它定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖它的对象都将得到通知。
@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更新。