咱们先看看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; } } 复制代码
/** 原来的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:{
callAnotherMethod:function*(){
return {wow:'changeWowValue'};
}
fetch:function*() {
const response = yield fakeChartData();
return response;
},
//这里稍作修改,演示了reducer方法内如何调用其余reducer方法
fetchSalesData:async function({state, moduleState, dispatch, payload}) {
console.log(sate, moduleState, payload);
//这里的dispatch若是不指定module和reducerModule,就隐含的是由最初的在cc实例里触发$$dispatch时计算好的module和reducerModule
await dispatch({type:'callAnotherMethod'});
const response = await fakeChartData();
const salesData = response.salesData;
return { salesData };
},
clear(){
const originalState = getInitialState();
return originalState;
}
}
}
复制代码
由上可以发现,cc里的setState
须要的state
和dispatch
对应函数返回的state
,都是react鼓励的部分state
,你须要改变哪一部分的state
,就仅仅把这一部分state
交给cc
就行了。同时cc也兼容redux
生态的思路,一切共享的数据源都从props
注入,而非存储在state
里。
由于全部的改变state的行为
都会通过$$changeState
,因此状态的变化依然是可预测的同时也是能够追踪的,后面cc的迭代版本里会利用immutable.js
,来让状态树能够回溯,这样cc
就能够实现时间旅行
的功能了,敬请期待.
注意哦! 如今我仅仅先把两个路由级别的组件交给cc处理, ant pro任然完美工做起立, 这两个路由文件是 routes/Dashboard/Analysis.js
和 routes/Forms/Basic.js
.
同时我也新增了一个路由组件 routes/Dashboard/CCState.js
来展现cc强大能力, 这个组件尚未完全写完,将会被持续更新的, 就像 我为cc专门写的引导示例同样,将会很快会为你们带来更多的精彩演示