sinooa-redux-crud
适用于中科软OA产品的CRUD业务的通用Redux实现。
sinooa-redux-crud实现:
- 业务的各种数据列表分页加载
- 业务数据的删除、新增、修改
- 草稿
- 删除、新增、修改与数据列表的同步
- 包括ActionTypes、Actions、Reducers、Sagas、Selectors
安装
先在项目中添加依赖
yarn add sinooa-redux-crud
或者
npm install --save sinooa-redux-crud
试用
采用redux管理状态,需要有ActionTypes、ActionCreators、reducer、saga和store。接下来,就用sinooa-redux-crud来实现redux状态管理。
首先通过create-react-app创建一个react应用,添加相关依赖,然后在这个demo项目中添加以下代码文件。
jfw.js
:
import { ActionTypes, ActionCreators, createReducers, createSagas } from 'sinooa-redux-crud';
import { create } from 'apisauce';
const http = create({
baseURL: '/api',
});
const moduleId = 'jfw';
const idPropertyName = 'id'; //业务数据中的id属性名
const listTypes = ['todos', 'related']; //列表类别
const baseUrl = '/spring/archives/out/jfw';
const actionTypes = new ActionTypes(moduleId, listTypes);
const actionCreators = new ActionCreators(moduleId, idPropertyName, listTypes);
const reducers = createReducers(moduleId, idPropertyName, listType);
const saga = createSagas(http, moduleId, baseUrl, idPropertyName, listType);
export { actionTypes, actionCreators, reducers, saga };
store.js
:
import { ActionTypes, ActionCreators, createReducers, createSagas } from 'sinooa-redux-crud';
import { createStore, applyMiddleware, compose, combineReducers } from 'redux';
import createSagaMiddleware from 'redux-saga';
import { create } from 'apisauce';
import { reducers, saga } from './jfw';
const sagaMiddleware = createSagaMiddleware();
const enhancer = compose(applyMiddleware(sagaMiddleware));
const store = createStore(combineReducers({
jfw: reducers,
}), enhancer);
sagaMiddle.run(rootSaga);
export default store;
components/JfwList.js
:
import React from 'react';
export default function({
items,
pageNo,
onRequestFetch,
}) {
return (
<div>
<button onClick={() => onRequestFetch(0) }>刷新</button>
<button onClick={() => onRequestFetch(pageNo + 1)}>加载下一页</buton>
<ul>{items.map(item => <li>{item.title}</li>)}</ul>
</div>);
}
containers/JfwList.js
:
import { connect } from 'react-redux';
import JfwList from '../components/JfwList';
import { actionCreators } from '../jfw';
const mapStateToProps = state => ({
items: state.jfw.todos.map(id => state.jfw.records[id]),
pageNo: state.jfw.todosPage.number,
});
const mapDispatchToProps = dispatch => ({
onRequestFetch: pageNo => dispatch(actionCreators.fetchTodos('userId', pageNo, 15))
});
export default connect(mapStateToProps, mapDispatchToProps)(JfwList);
App.js
:
import React from 'react';
import { Provider } from 'react-redux';
import JfwList from './containers/JfwList';
import store from './store';
export default function() {
return (<Provider store={store}>
<JfwList />
</Provider>);
}
规范示例
状态
一个crud业务(例如名称为jfw)的状态结构如下:
STATE_ROOT
|__ jfw
|___ records
|___ '1'
|___ id: '1'
|___ title: '开发sinooa-crud-redux@1.0.0'
|___ done: true
|___ '2'
|___ id: '2'
|___ title: '开发sinooa-workflow-redux@1.0.0'
|___ done: false
|___ '3'
|___ id: '3'
|___ title: '开发react-native-sinooa-workflow@1.0.0'
|___ done: false
|___ todos: ['2', '3']
|___ all: ['1', '2', '3']
|___ related: ['1', '2']
|___ draft: ['3']
|___ createStatus: 'DOING' | 'SUCCESS' | 'FAILURE' | 'NORMAL'
|___ createError: '创建失败的错误描述'
|___ deleteStatus: 'DOING' | 'SUCCESS' | 'FAILURE' | 'NORMAL'
|___ deleteError: '删除失败的错误描述'
|___ updateStatus: 'DOING' | 'SUCCESS' | 'FAILURE' | 'NORMAL'
|___ updateError: '更新失败的错误描述'
|___ fetchTodosStatus: 'FETCHING' | 'SUCCESS' | 'FAILURE' | 'NORMAL'
|___ fetchTodosError: '加载待办列表失败的描述'
|___ fetchTodosPageNo: 0
|___ todosPage
|___ number: 0 ---- 当前页数
|___ size: 15 ---- 一页包含项目的数量
|___ totalPages: 1 ---- 总页数
|___ totalElements: 3 ---- 总项目数目
|___ numberOfElements: 3 ---- 当前页包含项目的数量
|___ last: true ---- 最后一页
|___ first: true ---- 第一页
|___ todosFilterConditions: {} ----- 待办列表的过滤条件
|___ todosSort: [ ['createDate', 'desc'], ['updateDate', 'asc']] ------- 待办列表的排序设置
|___ fetchAllStatus: 'FETCHING' | 'SUCCESS' | 'FAILURE' | 'NORMAL'
|___ fetchAllError: '加载待办列表失败的描述'
|___ fetchAllPageNo: 0
|___ allPage
|___ number: 0 ---- 当前页数
|___ size: 15 ---- 一页包含项目的数量
|___ totalPages: 1 ---- 总页数
|___ totalElements: 3 ---- 总项目数目
|___ numberOfElements: 3 ---- 当前页包含项目的数量
|___ last: true ---- 最后一页
|___ first: tru ---- 第一页
|___ allFilterConditions: {} ----- 全部列表的过滤条件
|___ allSort: [ ['createDate', 'desc'], ['updateDate', 'asc']] ------- 全部列表的排序设置
|___ fetchRelatedStatus: 'FETCHING' | 'SUCCESS' | 'FAILURE' | 'NORMAL'
|___ fetchRelatedError: '加载待办列表失败的描述'
|___ fetchRelatedPageNo: 0
|___ relatedPage
|___ number: 0 ---- 当前页数
|___ size: 15 ---- 一页包含项目的数量
|___ totalPages: 1 ---- 总页数
|___ totalElements: 3 ---- 总项目数目
|___ numberOfElements: 3 ---- 当前页包含项目的数量
|___ last: true ---- 最后一页
|___ first: true ---- 第一页
|___ relatedFilterConditions: {} ----- 待办列表的过滤条件
|___ relatedSort: [ ['createDate', 'desc'], ['updateDate', 'asc']] ------- 待办列表的排序设置
关于业务的数据是存储在records
中的,这是一个业务主键id与业务数据对应的map。
列表中存储的是业务主键id,如todos: ['2', '3']
,related: ['1', '2']
。
动作
动作类型
以局发文(jfw)为例:
import { ActionTypes } from 'sinooa-crud-workflow';
const actionTypes = new ActionTypes('jfw');
获取方式 | 动作类型 | 动作类型描述 |
---|---|---|
actionTypes.createStart |
JFW_CREATE_START |
开始创建局发文 |
actionTypes.createSuccess |
JFW_CREATE_SUCCESS |
创建局发文成功 |
actionTypes.createFailure |
JFW_CREATE_FAILURE |
创建局发文失败 |
actionTypes.resetCreateStatus |
JFW_RESET_CREATE_STATUS |
重置创建状态 |
actionTypes.updateStart |
JFW_UPDATE_START |
开始更新局发文 |
actionTypes.updateSuccess |
JFW_UPDATE_SUCCESS |
更新局发文成功 |
actionTypes.updateFailure |
JFW_UPDATE_FAILURE |
更新局发文失败 |
actionTypes.resetUpdateStatus |
JFW_RESET_UPDATE_STATUS |
重置更新状态 |
actionTypes.deleteStart |
JFW_DELETE_START |
开始删除局发文 |
actionTypes.deleteSuccess |
JFW_DELETE_SUCCESS |
删除局发文成功 |
actionTypes.deleteFailure |
JFW_DELETE_FAILURE |
删除局发文失败 |
actionTypes.resetDeleteStatus |
JFW_RESET_DELETE_STATUS |
重置删除状态 |
actionTypes.fetchRecordStart |
JFW_FETCH_RECORD_START |
开始加载单个数据 |
actionTypes.fetchRecordSuccess |
JFW_FETCH_RECORD_SUCCESS |
加载单个数据成功 |
actionTypes.fetchRecordFailure |
JFW_FETCH_RECORD_FAILURE |
加载单个数据失败 |
actionTypes.resetFetchRecordStatus |
JFW_RESET_FETCH_RECORD_STATUS |
重置加载单个数据的状态 |
actionTypes.fetchTodosStart |
JFW_FETCH_TODOS_START |
开始加载待办列表 |
actionTypes.fetchTodosSuccess |
JFW_FETCH_TODOS_SUCCESS |
加载待办列表成功 |
actionTypes.fetchTodosFailure |
JFW_FETCH_TODOS_FAILURE |
加载待办列表失败 |
actionTypes.resetFetchTodosStatus |
JFW_RESET_FETCH_TODOS_STATUS |
重置加载待办列表的状态 |
actionTypes.fetchAllStart |
JFW_FETCH_ALL_START |
开始包含所有数据的列表 |
actionTypes.fetchAllSuccess |
JFW_FETCH_ALL_SUCCESS |
加载包含所有数据的列表成功 |
actionTypes.fetchAllFailure |
JFW_FETCH_ALL_FAILURE |
加载包含所有数据的列表失败 |
actionTypes.resetFetchAllStatus |
JFW_RESET_FETCH_ALL_STATUS |
重置加载包含所有数据的列表状态 |
actionTypes.fetchReleatedStart |
JFW_FETCH_RELATED_START |
开始加载相关数据列表 |
actionTypes.fetchRelatedSuccess |
JFW_FETCH_RELATED_SUCCESS |
加载相关数据列表成功 |
actionTypes.fetchRelatedFailure |
JFW_FETCH_RELATED_FAILURE |
加载相关数据列表失败 |
actionTypes.resetFetchRelatedStatus |
JFW_RESET_FETCH_RELATED_STATUS |
重置加载相关数据列表状态 |
actionTypes.fetchDraftStart |
JFW_FETCH_DRAFT_START |
开始加载草稿数据列表 |
actionTypes.fetchDraftSuccess |
JFW_FETCH_DRAFT_SUCCESS |
加载草稿数据列表成功 |
actionTypes.fetchDraftFailure |
JFW_FETCH_DRAFT_FAILURE |
加载草稿数据列表失败 |
actionTypes.resetFetchDraftStatus |
JFW_RESET_FETCH_DRAFT_STATUS |
重置加载草稿列表状态 |
actionTypes.resetRecordStatus |
JFW_RESET_RECORD_STATUS |
重置单条业务数据相关的状态 |
动作创建器
以局发文(jfw)为例:
import { ActionCreators } from 'sinooa-crud-redux';
const actionCreators = new ActionCreators('jfw', 'id');
动作创建器 | 动作类型 | 动作类型描述 |
---|---|---|
actionCreators.createStart(record) |
JFW_CREATE_START |
开始创建局发文 |
actionCreators.createSuccess(record) |
JFW_CREATE_SUCCESS |
创建局发文成功 |
actionCreators.createFailure(error) |
JFW_CREATE_FAILURE |
创建局发文失败 |
actionCreators.resetCreateStatus() |
JFW_RESET_CREATE_STATUS |
重置创建状态 |
actionCreators.updateStart(record) |
JFW_UPDATE_START |
开始更新局发文 |
actionCreators.updateSuccess(record) |
JFW_UPDATE_SUCCESS |
更新局发文成功 |
actionCreators.updateFailure(record) |
JFW_UPDATE_FAILURE |
更新局发文失败 |
actionCreators.resetDeleteStatus() |
JFW_RESET_DELETE_STATUS |
重置删除状态 |
actionCreators.deleteStart(id) |
JFW_DELETE_START |
开始删除局发文 |
actionCreators.deleteSuccess(id) |
JFW_DELETE_SUCCESS |
删除局发文成功 |
actionCreators.deleteFailure(error) |
JFW_DELETE_FAILURE |
删除局发文失败 |
actionCreators.resetDeleteStatus() |
JFW_RESET_DELETE_STATUS |
重置删除状态 |
actionCreators.fetchRecordStart(id) |
JFW_FETCH_RECORD_START |
开始加载单个数据 |
actionCreators.fetchRecordSuccess(record) |
JFW_FETCH_RECORD_SUCCESS |
加载单个数据成功 |
actionCreators.fetchRecordFailure(error) |
JFW_FETCH_RECORD_FAILURE |
加载单个数据失败 |
actionCreators.resetFetchRecordStatus() |
JFW_RESET_FETCH_RECORD_STATUS |
重置加载单个数据的状态 |
actionCreators.fetchTodosStart(userId, pageNo, pageSize, filterCondition?, sort?) |
JFW_FETCH_TODOS_START |
开始加载待办列表 |
actionCreators.fetchTodosSuccess(page) |
JFW_FETCH_TODOS_SUCCESS |
加载待办列表成功 |
actionCreators.fetchTodosFailure(error) |
JFW_FETCH_TODOS_FAILURE |
加载待办列表失败 |
actionCreators.resetFetchTodosStatus() |
JFW_RESET_FETCH_TODOS_STATUS |
重置加载待办列表的状态 |
actionCreators.fetchAllStart(userId, pageNo, pageSize, filterCondition?, sort?) |
JFW_FETCH_ALL_START |
开始包含所有数据的列表 |
actionCreators.fetchAllSuccess(page) |
JFW_FETCH_ALL_SUCCESS |
加载包含所有数据的列表成功 |
actionCreators.fetchAllFailure(error) |
JFW_FETCH_ALL_FAILURE |
加载包含所有数据的列表失败 |
actionCreators.resetFetchAllStatus() |
JFW_RESET_FETCH_ALL_STATUS |
重置加载包含所有数据的列表状态 |
actionCreators.fetchReleatedStart(userId, pageNo, pageSize, filterCondition?, sort?) |
JFW_FETCH_RELATED_START |
开始加载相关数据列表 |
actionCreators.fetchRelatedSuccess(page) |
JFW_FETCH_RELATED_SUCCESS |
加载相关数据列表成功 |
actionCreators.fetchRelatedFailure(error) |
JFW_FETCH_RELATED_FAILURE |
加载相关数据列表失败 |
actionCreators.resetFetchRelatedStatus() |
JFW_RESET_FETCH_RELATED_STATUS |
重置加载相关数据列表状态 |
actionCreators.fetchDraftStart(userId, pageNo, pageSize, filterCondition?, sort?) |
JFW_FETCH_DRAFT_START |
开始加载草稿数据列表 |
actionCreators.fetchDraftSuccess(page) |
JFW_FETCH_DRAFT_SUCCESS |
加载草稿数据列表成功 |
actionCreators.fetchDraftFailure(error) |
JFW_FETCH_DRAFT_FAILURE |
加载草稿数据列表失败 |
actionCreators.resetFetchDraftStatus() |
JFW_RESET_FETCH_DRAFT_STATUS |
重置加载草稿列表状态 |
actionTypes.resetRecordStatus(recordId) |
JFW_RESET_RECORD_STATUS |
重置单条业务数据相关的状态 |
说明:
-
filterCondition
- 表示过滤条件。形如{ title: '标题' }
-
sort
- 指定排序规则。形如[['startDate', 'desc'], ['endDate, 'asc']]
API说明
创建reducer
import { createReducer } from 'sinooa-redux-crud';
const moduleId = 'fawen';
const idPropertyName = 'id';
const listTypes = [{
name: 'todos',
onlySaveCurrentPageData: true,
saveDataToRecords: true,
}, 'related', 'all'];
const reducer = createReducer(moduleId, idPropertyName, listTypes);
重点介绍一下listTypes
的配置。listTypes
是用来定义数据列表的。它是一个数组。数据中包含对数据列表的定义,这些定义包括:
-
name
- 数据列表的名称 -
onlySaveCurrentPageData
- 是否只存储当前加载页的数据。默认为false
,表示存储所有页的数据。 -
saveDataToRecords
- 保存数据到公共的records
中。默认为true
。 -
onDeleteRecord
- 当发生删除操作时,列表需要做的响应 -
onUpdateRecord
- 当发生数据更新操作时,列表需要做的响应 -
onCreateRecord
- 当创建数据时,列表需要做出的响应