regularjs X redux
import ReduxUtil from 'regular-redux-util';
// 外层组件
let WrapperComponent = BaseComponent.extend({
// ...
config(data) {
// 初始化&绑定 store
data.store = ReduxUtil.createStore(
reducer, // reducer
initialState, // 可选。普通对象{},为store设置一个初始值
// 可选。中间件,默认值为: thunk 和 promise
[ReduxUtil.middleware.thunk, ReduxUtil.middleware.promise],
// 可选。其他可配置参数
{
isDev: window.DEBUG, // 可选。当前是否为开发环境
disableImmutableJS: false // 可选。是否不支持immutable,默认为false,表示支持
}
);
},
//...
});
// 为外层组件添加 StoreProvider
WrapperComponent.use(ReduxUtil.StoreProvider);
- reducer:符合redux定义的一个reducer函数 or 对象(使用多个reducer时)。注意:reducer 函数要由 ReduxUtil.createReducer 创建。对象可以由多个reducer函数拼装得到。
- initialState:可选。普通对象{},为store设置一个初始值。
- 建议一定要写,开发时可以很方便的查看state的数据结构
- middlewares:可选,中间件,默认会提供 thunk 和 promise 中间件。当有值的时候,默认会被覆盖。在开发环境还会提供logger中间件。
- promise中间件api参考自:https://github.com/pburtchaell/redux-promise-middleware 5.x版
- 开发环境的判断见下面的第4点
- config:可选,一些配置参数。
- isDev:可选。当前是否为开发环境。默认值由:window.DEBUG || /localhost|\d+.\d+.\d+.\d+/.test(location.hostname) 来确定
- disableImmutableJS:可选。是否不支持immutable,默认为false,表示支持
<!-- 外层包裹 StoreProvider,并绑定 store 字段 -->
<StoreProvider store={store}>
<sub-component></sub-component>
<sub-component2></sub-component2>
...
</StoreProvider>
// action-factory.js
import ReduxUtil from 'regular-redux-util';
// 方式一
let actions;
const actionObj = {
// 图片模块,切换主图
changeImg(bigImg, selectIndex) {
return {
payload: {
bigImg, selectIndex
}
};
}
};
actions = ReduxUtil.createAction(actionObj);
// 方式二
let actions = ReduxUtil.createAction({});
actions.add('updateAddress', address => { payload: address });
// 1、使用 promise 中间件( payload 的值为一个 promise )
// 获取地址
getAddress(query = {}) {
return {
payload: service.getAddress(query)
.then((json = {}) => {
return (json.retcode === 200) ?
Promise.resolve(json.addressDto || C.DEF_ADDRESS) :
Promise.resolve(C.DEF_ADDRESS);
})
.catch((err) => {
return Promise.resolve(C.DEF_ADDRESS);
})
};
}
// 2、如果参数需要从 state 中获取,可以 thunk 和 promise 中间件结合使用
// 获取异步数据
initAsyncData(address) {
// thunk action
return (dispatch, getState) => {
let state = getState();
let query = Object.assign({}, address, {
goodsId: state.get('goodsId'),
categoryId: state.get('categoryId')
});
// promise action
return dispatch(actions.initAsyncDataService(query));
};
},
// 真正的promise类型的action
initAsyncDataService(query) {
return {
payload: service.getAsyncData({
query
})
.then((json = {}) => {
if (json.code === 200) {
return Promise.resolve(json.data);
}
return Promise.reject(json || {});
})
.catch((err) => {
return Promise.reject(err);
})
};
}
- api参考自:https://github.com/pburtchaell/redux-promise-middleware 5.x版
- dispatch该action之后,会立即触发所绑定的reducer中的pedding
- promise对象如果返回Promise.resolve,则触发所绑定的reducer中的fulfilled
- promise对象如果返回Promise.reject,则触发所绑定的reducer中的rejected
- 后面会介绍如何定义reducer,以及pedding、fulfilled、rejected的reducer处理函数
import ReduxUtil from 'regular-redux-util';
// 之前创建的action
import {
changeImg, // 普通
getAddress, // 异步
initAsyncDataService // 异步
} from '../action-factory';
// reduer 实例
let reducer = ReduxUtil.createReducer();
// 普通的action绑定的reducer
reducer.add(changeImg, (state, action) => {
return state;
});
// 一个异步action绑定的3个reducer
reducer.add(getAddress.pending, (state, action) => {
// ...
return state;
});
reducer.add(getAddress.fulfilled, (state, action) => {
// ...
return state;
});
reducer.add(getAddress.rejected, (state, action) => {
// ...
return state;
});
// 另一种写法,集合在一起
reducer.add(getAddress, {
pending(state, action) => state,
fulfilled(state, action) => state,
rejected(state, action) => state,
});
// 另另种,将错误的情况统一处理
reducer.add([
getAddress.rejected,
initAsyncData.rejected,
], state => {
//...
return state;
});
reducer就是一个纯函数,为了更好的复用,可以在一个reducer中调用其他reducer
import ReduxUtil from 'regular-redux-util';
// 之前创建的action
import {
changeImg, // 普通
getAddress, // 异步
initAsyncDataService // 异步
} from '../action-factory';
// ...
// 异步数据获取成功
reducer.add(initAsyncDataService.fulfilled, (state, action = {}) => {
// ... bigImg, selectIndex
// 切换主图
// 调用 changeImg 绑定的 reducer
// - 注意:返回值要赋值给 state,否则无法修改state的结果
// - 第三个参数:{bigImg, selectIndex},是传入到`changeImg`这个action的第二个参数(action对象)
state = reducer.exec(changeImg, state, {bigImg, selectIndex});
// ...
return state;
});
// ...
container组件,就是绑定了redux的regular组件。
当redux的state发生变化时(dispatch了action,触发reducer之后,state就应当发生变化),会触发所有container组件的更新。
更新:拉取新的state上的数据,更新regular中的data变量,最后调用 this.$update()
。
一个container组件的创建步骤如下:
import ReduxUtil from 'regular-redux-util';
import {
initSyncData,
getAddress,
initAsyncData,
triggerVidList,
buyNow
} from '../action-factory';
let Common = DetailComponent.extend({
template,
data: {
selVidList: [] // 已选择的sku
},
config() {},
init() {
},
//...
});
export default Comment;
修改export default Comment;
// 组件对象 `Comment`
export default ReduxUtil.connect({
mapState(state) {
// 已选择的sku
let selVidList = state.getIn(['skuProps', 'selVidList']);
return {
selVidList
};
},
// 【可选】state改变后是否触发组件的刷新
listenStateChange: ['skuProps', 'async.goodsCouponList'],
// 【可选】绑定到this上的action
mapDispatch: [
initSyncData,
getAddress,
initAsyncData,
triggerVidList,
buyNow
],
//【可选】
options: {
// 自动emit事件:
// this.$emit('rdx-connected')
// this.$emit('rdx-afterchanged')
// this.$emit('rdx-injected')
emit: true
}
})(Common);
- mapState:state改变后,将会执行所有组件的mapState函数。函数的参数是redux的state。函数的返回值
newMappedState
将会使用newMappedState && Object.assign(this.data, newMappedState);
更新组件的data,并最终通过this.$udpate()
更新组件的视图。注意,当使用不可变数据类型(disableImmutableJS: false)的时候,state是一个immutable对象。 - listenStateChange:【该参数可选】mapState的模式可能会引起性能问题。因为reducer触发了state更新之后,会触发所有container组件的mapState。因此可以通过
listenStateChange
参数来做一次筛选,只有当listenStateChange
中的数据发生变化时,才会执行mapState
函数。比如在上面的例子里,只有当state.skuProps
或state.async.goodsCouponList
发生改变的时候,才会执行mapState
函数。 - mapDispatch:【该参数可选】container组件中,如果需要dispatch一个action,那么需要将这个action函数写到
mapDispatch
这个参数下面。比如initSyncData
然后就可以通过this.initSyncData(window.__syncData || {});
来dispatch这个action了。如果不使用mapDispatch
参数,也可以直接适用this.$dispatch
,见下文步骤4。 - options:{emit: boolean}:【该参数可选】emit参数默认为false。当emit设置为true的时候,会在cotainer组件的生命周期中,自动emit三个事件。
- rdx-connected:只触发一次,在首次
mapState
执行之后 - rdx-afterchanged:每次执行
mapState
后都会触发 - rdx-injected:只执行一次,当组件可以获取
this.$refs.xx
的之后触发。如果组件被实例化多次,也会被触发多次。
- rdx-connected:只触发一次,在首次
// 方法一
...
init() {
// dispatch action
this.$dispatch(initSyncData(window.__syncData || {}));
},
...
// 方法二
// 如果在上一步`ReduxUtil.connect`中使用了mapDispatch
// 并注册了`initSyncData`这个action,那么可以使用方法二,如下:
...
init() {
// dispatch action
this.initSyncData(window.__syncData || {});
},
...
import Common from './redux-base/container/common';
WrapperComponent.component('Common', Common);
<StoreProvider store={store}>
...
<Common
on-loaded={this.loaded($event)}
on-refreshTopBar={this.refreshTopBar($event)}
></Common>
...
</StoreProvider>
到这里,使用步骤就介绍完了