Redux拆分
公共Redux目录及组织方式
目录位置选择
- src
- assets
- components
- pages
- share
- course.js
- pay.js
关于公共的redux应该放置的位置和命名应该结合项目结构考虑,在我们的h5项目中,公共的库在assets中,公共组件在components中,所以建议将公共的redux也放置src目录下,并且命名为share(或者redux,具体名字可以再商讨)。
action、reducer组织方式
按模块进行划分(推荐)
目录结构如下:
- share
- course.js
- pay.js
以course.js为例,course.js中包含于course相关的模块的actionType、actionCreator、defaultState、reducer等,这种方式是按照模块来划分redux,把相关的代码写在一起,模块性更强。
/** actionType **/
const GET_COURSE_DETAIL = 'GET_COURSE_DETAIL';
/** actionCreator **/
const getCourseDetail = function(result) {
dispatch({
type: GET_COURSE_DETAIL,
data: result.pageInfo,
});
};
/** defaultState **/
const defaultState = {};
/** reducer **/
function courseReducer(state = {}, action) {
switch (action.type) {
case GET_COURSE_DETAIL:
return {
...state,
...action.data,
};
default:
return state;
}
}
export default courseReducer;
export {
getCourseDetail,
defaultState,
};
函数功能划分
按照函数公共划分,即按照actionType、actionCreator、reducer将文件分为actioncreator、action_type、reducer。这种方式因为函数功能类似,所以结构较清晰,但是并不利于后期的维护,因为添加一个action需要在三个文件中做更改。代码略,目录结构如下图:
- share
- action_creator.js
- action_type.js
- reducer.js
公共Redux的使用
公共redux的目录及代码组织方式默认是上面方法一种的方式,如果采用方法二的方式,只需要以另外一种方式取得actionCreatore函数、reducer函数即可。
actionCreator的使用
在页面的index.jsx中,在通过react-redux库的connect方法,连接action,store和container时,组合公共的actionCreator方法
import { Provider, connect } from 'react-redux';
import {
getCourseDetail,
} from 'share/course'; // 取出需要用的公共的actionCreator方法
import action from './action_creators';
import Container from './Container';
const actions = { // 组合本页面的、公共redux的、公共组件的actionCreator方法
...action,
getCourseDetail,
};
const Root = connect(
state => state,
actions
)(Container);
reducer的使用
reducer的应用影响了state的结构,所以这里要慎重一些,在页面的reducer.js中组合reducer
combineReducer
import { combineReducers } from 'redux';
import courseReucer from 'share/course';
import { dialogReducer } from 'components/dialog';
import { GET_DATA } from './action_types';
function reducer(state = {}, action) {
switch (action.type) {
case GET_DATA:
return { ...state, ...action.data };
default:
return state;
}
}
export default combineReducers({
course: courseReucer,
dialog: dialogReducer,
pageInfo: reducer,
});
// 页面state,及根container的this.props数据结构
{
course: { cid: 123 }, // 通过getCourseDetail方法获取的值
dialog: { title: '确认' }, // Dialog对话框相关的值
pageInfo: { some: 'somevaule' } // 页面本身redux获取的值
}
第一种方式是直接使用redux官方提供的combineReducer。这种方式比较有局限的一点是,每个reducer模块对应的state的数据都要对应一个对象,所以要取值的话,在组件中要使用诸如this.props.pageInfo这样的方式去取每个数据块的值,且相关之间不能做到数据共享。这样做的好处也很明显,就是数据分块明显,利于理解。
自定义组织reducer
import courseReucer from 'share/course';
import { dialogReducer } from 'components/dialog';
import { GET_DATA } from './action_types';
function reducer (state = {}, action) {
const course = courseReucer(state, action);
state = {
...state,
...course,
dialog: dialogReducer(state.dialog, action),
};
switch (action.type) {
case GET_DATA:
return { ...state, ...action.data };
default:
return state;
}
}
export default reducer;
// 页面state,及根container的this.props数据结构
{
cid: 123, // 通过getCourseDetail方法获取的值,在this.props顶层
dialog: { title: '确认' }, // Dialog对话框相关的值
some: 'somevaule' // 页面本身redux获取的值,在this.props顶层
}
combineReducer只是简单的将reducer进行组合而已,自己实现也是完全OK的。如上图的方式,我们可以选择将公共的redux模块的数据放在this.props中(如courseReducer),也可以选择将其放到this.props的一个对象中(如dialogReucer)。由于我们原有结构不适合使用combineReducer,应为改造成本高,所以建议使用自定义组织reducer模式。
公共组件redux
Dialog组件代码结构
有些公共组件需要用到数据,譬如dialog组件,这种组件建议自带redux模块,此处以dialog组件为例。
import React, { Component } from 'react';
import classNames from 'classnames';
import {
showDialog,
hideDialog,
} from './action_creator';
import reducer from './reducer';
import './index.scss';
class Dialog extends Component {
// 业务逻辑省略
}
export default Dialog;
export {
showDialog,
hideDialog,
reducer as dialogReducer,
};
Dialog组件使用
// 页面index.jsx
import {
getCourseDetail,
} from 'share/course';
import {
showDialog,
hideDialog,
} from 'components/dialog';
import action from './action_creators';
const actions = {
...action,
getCourseDetail,
showDialog,
hideDialog,
};
// 页面reducer.js
import { dialogReducer } from 'components/dialog';
function reducer (state = {}, action) {
state = {
...state,
course: courseReucer(state.courseDetail, action),
dialog: dialogReducer(state.dialog, action),
};
// 业务逻辑略
}
// 页面container.jsx
class Container extends Component{
showDialog = () => {
this.props.showDialog({
title: '报名成功',
titleClass: 'succ',
leftBtnName: '确定',
leftBtnIsPrimary: false,
leftBtnClick: this.props.hideDialog,
});
}
render() {
return(
<div>
<div onClick={this.showDailog}>点击显示弹框</div>
<Dialog {...props.dialog} />
</div>
)
}
}