629960
Ant Design Pro Powered by react-control-center
cc版本的ant-design-pro来了,ant-design-pro powered by C_C
我们先看看redux是如何工作起来的,在来细比较cc和redux的最大的不同之处
redux如何工作?订阅redux单一状态树里的部分数据源,让组件被redux接管,从而实现当订阅的数据源发生变化时才触发渲染的目的
- 我们知道,在
redux世界里,可以通过一个配置了mapStateToProps的connect高阶函数去包裹一个组件,能够得到一个高阶组件,该高阶组件的shouldComponentUpdate会被redux接管,通过浅比较this.props!==nextProps来高效的决定被包裹的组件是否要触发新一轮的渲染,之所以能够这么直接进行浅比较,是因为在redux世界的reducer里,规定了如果用户改变了一个状态某一部分值,一定要返回一个新的完整的状态,如下所示,是一个传统的经典的connect和reducer写法示例。
/** code in component/Foo.js, connect现有组件,配置需要观察的`store`树中的部分state,绑定`action` */
class Foo extends Component{
//...
render(){
return (
<div>
<span>{this.props.fooNode.foo}</span>
<button onClick={this.props.actions.incFoo}>incFoo</button>
</div>
);
}
}
export default connect(
state => ({
list: state.list,
fooNode: state.fooNode,
}),
dispatch => ({
actions: bindActionCreators(fooActionCreator, dispatch)
})
)(Foo)
/** code in action/foo.js, 配置action纯函数 */
export const incFoo = () =>{
return {type:'INC_FOO'};
}
/** code in reducer/foo.js, 定义reducer函数 */
function getInitialState() {
return {
foo: 1,
bar: 2,
};
}
export default function (state = getInitialState(), action) {
switch (action.type) {
case 'INC_FOO': {
state.foo = state.foo + 1;
return {...state};
}
default:{
return state;
}
}
}
- 在
ant-design-pro的dva世界里,dva将redux做了一层浅封装,省去了繁琐的定义action函数,connect时要绑定action函数等过程,给了一个命名空间的概览,一个命名空间下可以定义state、effects、reducers这些概念,组件内部dispatch的action对象的type的格式形如${namespaceName}/${methodName},这样dva就可以通过解析用户调用dispatch函数时派发的action对象里的type值而直接操作effects里的函数,在effects里的某个函数块内处理完相应逻辑后,用户可以调用dva提供给用户的put函数去触发reducers里的对应函数去合成新的state,尽管流程上简化了不少,但是归根到底还是不能脱离redux的核心理念,需要合成一个新的state! 以下示例是ant-design-pro里一个经典的变种redux流程写法.
/** code in component/Foo.js, connect现有组件,配置需要观察的`store`树中的部分state */
import { connect } from 'dva';
class Foo extends Component{
//...
render(){
return (
<div>
<span>{this.props.fooNode.foo}</span>
<button onClick={()=>this.props.dispatch({type:'fooNode/incFoo', payload:2})}>incFoo</button>
</div>
);
}
}
export default connect(
state => ({
list: state.list,
fooNode: state.fooNode,
})
)(Foo)
/** code in models/foo.js */
import logService from '@/services/log';
export default {
namespace: 'fooNode',
state: {
foo: 1,
bar: 1,
},
effects: {
*query({ payload:incNumber }, { call, put }) {
yield call(logService, incNumber);
yield put({
type: 'saveFoo',
payload: incNumber,
});
},
},
reducers: {
saveFoo(state, action) {
return { ...state, foo:action.payload };
},
},
};
cc如何工作?订阅react-control-center的部分数据源,当这些部分数据源任意一个部分发生变化时,cc主动通知该组件触发渲染
cc和redux最大的不同就是,cc接管了所有cc组件的具体引用,当用户的react组件注册成为cc组件时,cc的register函数需要用户配置ccClassKey、module、sharedStateKeys、globalStateKeys、stateToPropMapping等参数来告诉cc怎么对这些具体的引用进行分类,然后cc就能够高效并精确的通知哪些cc组件实例能够发生新一轮的渲染。- 实际上当你在
cc组件实例里调用this.setState时,效果和原有的this.setState毫无差别,但是其实cc组件实例的this.setState已近不再是原来的了,这个函数已经被cc接管并做了相当多的工作,原来的已经被cc保存为reactSetState,当你调用cc组件实例的this.setState,发生的事情大概经过了以下几步 - 因为此文主要是介绍和证明cc 的弱入侵性和灵活性,而
ant-design-pro里的组件的state并不需要被接管,所以我们下面的示例写法仅仅使用cc.connect函数将组件的状态和cc.store打通,这些状态并非从state里取,而是从this.$$propState里获取,下面的示例注释掉的部分是原dva写法,新增的是cc的写法. - (备注:此处仅仅展示关键代码详细代码见 )
/** code in src/routes/Dashboard/Analysis.js, */
import React, { Component } from 'react';
// import { connect } from 'dva';
import cc from 'react-control-center';
// @connect(({ chart, loading }) => ({
// chart,
// loading: loading.effects['chart/fetch'],
// }))
@cc.connect('Analysis', {
'chart/*': '',
'form/*': '', // this is redundant here, just for show isPropStateModuleMode's effect
}, { isPropStateModuleMode: true })
export default class Analysis extends Component {
state = {
loading: true,
salesType: 'all',
currentTabKey: '',
rangePickerValue: [],
}
componentDidMount() {
this.$$dispatch({
module: 'chart', type: 'fetch'
}).then(() => this.setState({ loading: false }));
// this.props.dispatch({
// type: 'chart/fetch',
// }).then(() => this.setState({ loading: false }));
}
componentWillUnmount() {
// const { dispatch } = this.props;
// dispatch({
// type: 'chart/clear',
// });
// this.$$dispatch({ module: 'chart', type: 'clear' });
}
handleRangePickerChange = (rangePickerValue) => {
this.setState({
rangePickerValue,
});
// this.props.dispatch({ type: 'chart/fetchSalesData'});
this.$$dispatch({ module: 'chart', type: 'fetchSalesData' });
}
selectDate = (type) => {
this.setState({
rangePickerValue: getTimeDistance(type),
});
// this.props.dispatch({ type: 'chart/fetchSalesData' });
this.$$dispatch({ module: 'chart', type: 'fetchSalesData' });
}
render() {
const { rangePickerValue, salesType, currentTabKey, loading } = this.state;
console.log('%c@@@ Analysis !!!', 'color:green;border:1px solid green;');
const {
visitData,
visitData2,
salesData,
searchData,
offlineData,
offlineChartData,
salesTypeData,
salesTypeDataOnline,
salesTypeDataOffline,
} = this.$$propState.chart;
// } = this.props.chart;
}
}
- models的替换
/** 原来的model,code in src/models/chart */
export default {
namespace: 'chart',
state: {
visitData: [],
visitData2: [],
salesData: [],
searchData: [],
offlineData: [],
offlineChartData: [],
salesTypeData: [],
salesTypeDataOnline: [],
salesTypeDataOffline: [],
radarData: [],
},
effects: {
*fetch(_, { call, put }) {
const response = yield call(fakeChartData);
yield put({
type: 'save',
payload: response,
});
},
*fetchSalesData(_, { call, put }) {
const response = yield call(fakeChartData);
yield put({
type: 'save',
payload: {
salesData: response.salesData,
},
});
},
},
reducers: {
save(state, { payload }) {
return {
...state,
...payload,
};
},
setter(state, { payload }) {
return {
...state,
...payload,
};
},
clear() {
return {
visitData: [],
visitData2: [],
salesData: [],
searchData: [],
offlineData: [],
offlineChartData: [],
salesTypeData: [],
salesTypeDataOnline: [],
salesTypeDataOffline: [],
radarData: [],
};
},
},
};
/** cc定义的model,code in src/cc-models/chart */
function getInitialState() {
return {
wow: 'wow',
visitData: [],
visitData2: [],
salesData: [],
searchData: [],
offlineData: [],
offlineChartData: [],
salesTypeData: [],
salesTypeDataOnline: [],
salesTypeDataOffline: [],
radarData: [],
}
}
export default {
module:'',
state:getInitialState(),
reducer:{
*fetch() {
const response = yield fakeChartData();
return response;
},
*fetchSalesData() {
const response = yield fakeChartData();
const salesData = response.salesData;
return { salesData };
},
clear(){
const originalState = getInitialState();
return originalState;
}
}
}