react基于mobx和ant封装数据状态管理工具

本文探讨了如何基于React、MobX和Ant Design封装数据状态管理工具,详细解析了数据状态管理的原理,介绍了数据状态管理工具的基础思想、属性和事件。文章还展示了如何封装表格功能,包括数据源、表格勾选和删除操作。此外,还涵盖了代码封装,如DataSet方法、HTTP请求、表格、表单、LOV、Input输入框和分页组件的实现,并给出了具体的demo示例。
摘要由CSDN通过智能技术生成

1.数据状态管理工具原理解析

数据状态管理工具的原理是通过mobx创建一个“可观察”的数据,然后使用mobx中名为 observer 的方法包裹组件,将我们封装的组件变成观察者,成为观察者组件。

成为观察者组件,当组件内使用的“可观察数据”发生改变时,会触发render,更新视图。从而实现数据驱动视图的功能。

2.数据状态管理工具封装基础思想

在封装数据状态管理工具的时候,我们可以将其封装成一个类或者一个函数,我采用的是将其封装成一个函数,传参是ds的配置数据,然后返回一个可观察的数据状态管理工具数据实例。

// props中传ds的配置数据
const DataSet = (props)=>{
  return observable({...});
}
​
//使用时先定义一个ds的配置数据,类似
const ds = {
    data:[...],
    fields:[...],
    queryFields:[...],
    transport: {...},
}
​
//组件内使用时
 constructor(props) {
        super(props);
        this.ListDs = DataSet(ds);
}
    
//封装ant-design表格,使其适配自己封装的DS。

3.简易数据状态管理工具基础属性

DataSet Porps

参数 说明
data 源数据
autoQuery 是否自动查询
autoCreate 初始化时自动创建一条记录
selection 表格选择模式,多选(multiple),单选(single),无(false)
fields 字段属性数组
queryFields 查询字段属性数组
transport 基础增删改查接口配置
events 事件监听,目前只增加了值更新事件update,后续可以继续加其他事件监听
pageSize 每页数据长度

DataSet Values

参数 说明
records 所有记录
queryDataSet 查询数据源
loading 加载中状态,用于表格表单查询时设置表格loading
fields ds表格字段配置信息,(目前暂时未对fields做其他处理)
selection 选择的模式, 可选值: false 'multiple' 'single'
validateStatus 整个records数据的校验状态
originalData 原始数据
totalElements 总数据条数
number 当前查询的第几页
size 每页查询多少条
totalPages 总页面数
current 获取或者设置当前记录
selected 选中的数据
queryBar 查询字段长度
dirtyRecords 状态发生改变的记录
created 新建的记录
updated 更新的记录
destroyed 临时删除的记录
dirty 数据状态是否发生改变

DataSet Methods

参数 说明
toData 将可观察的记录转化为普通数据
create 创建一条新记录
reset 重置dataset的数据状态
query 查询方法,可传 :(页数, 每页数据长度),例query(1, 20)
deleteAll 传参:想要删除的记录集,例:deleteAll(reocrds),删除选中的记录
remove 临时删除某些记录,将状态标为删除状态,通过submit提交进行删除
unSelectAll 表格取消全选
selectAll 表格全选
select 选中某条记录
unSelect 取消选中某条记录
submit 对新增,删除,更新状态的数据进行提交,然后重新查询
setQueryParams 设置查询参数,例:setQueryParams({name: '张三'})

DataSet Events

参数 说明
update 值更新事件,参数有:字段名name,更新值value,记录record,例:update: ({name, value, record})=>{}

Record Values

参数 说明
__status 状态, 可选值 add | update | delete | sync
__key 唯一键,主键
selection 是否选中
originalData 初始数据
validateStatus 校验状态
dirty 数据状态是否发生改变

Record Methods

参数 说明
reset 重置数据
validate 传dataset的fields参数,进行当前record的数据校验
toData 将可观察的记录转化为普通数据
get 获取某个字段的数据
set 修改某个字段数据

Field Props

参数 说明
name 字段名
label 字段标签名
type 类型
required 是否必输
dataSet Lov数据源
textField Lov显示字段名
valueField Lov值字段名
required 是否必输
defaultValue 默认值
options 值集数据
disabled 是否可编辑
min 数值输入框最小值校验
max 数值输入框最大值校验
validate ({value}) => string,自定义校验

Transport

参数 说明
read 查询接口配置
update 更新接口配置
create 新增接口配置
destroyed 删除接口配置

4.基于表格基础功能封装DS

(1)为表格提供可观察的数据源。

这里为表格提供了records属性,records属性包含所有的可观察数据,DataSet方法可接收一个ds的data属性,或者使用query方法,调用接口获取源数据。

const DataSet = (props)=>{
    const { data = [], transport } = props;
    let records: any[] = []; // 记录数据集
    const toObserveArrayData=(sourceData)=>{ //将源数据转化为可观察数据
       return sourceData.map(item => {
            return observable({
                data: item,
                ...other
            })
        });
    }
    
     function query() {// DS.query()方法,通过访问transport中read方法,返回查询的配置参数包含URL和查询参数。
        if (transport.read) {
            const queryData = this.queryDataSet.current.toData();
            const param = transport.read({dataSet, data: queryData});
            httpGet(param.url, {...param.data}).then(res => {// httpGet:axios get接口请求调用
                if (res && !res.failed) { // 固定后端接口返回格式,如果接口报错,返回falied和message
                    this.originalData = res.data;
                    this.records = toObserveArrayData(res.data);// 将接口返回数据转换为可观察数据并赋给records
                }
            })
        }
    }
  records = toObserveArrayData(data);//初始化records
  return observable({ // 返回一个可观察的DS实例对象
      records,
      query,
      ...other
  });
}
//other: 其他属性和方法。
​
//ant表格封装使用
 this.ListDs = DataSet(ds);
const {records}= this.ListDs;
<Table dataSource ={records} />

(2)实现表格勾选功能

首先为每一条record提供selection(false:未勾选,true:已勾选)属性。

const DataSet = (props)=>{
    const toObserveArrayData=(data)=>{ //将源数据转化为可观察数据
       return data.map(item => {
            return observable({
                data,
                selection:false,
                ...other
            })
        });
    }
    
     // 取消所有选择数据
    function unSelectAll() {
        this.records.map((item: { selection: boolean; }) => item.selection = false);
    }
    
    // 选择所有数据
    function selectAll() {
        this.records.map((item: { selection: boolean; }) => item.selection = true);
    }
​
    // 取消选择某些数据,传参:选中的记录集
    function unSelect(selectedRecords: any[]) {
        selectedRecords.map(item => item.selection = false);
    }
    
     // 选择某些数据,传参:选中的记录集
    function select(selectedRecords: any[]) {
        selectedRecords.map(item => item.selection = true);
    }
    
    //初始化records
     records = toObserveArrayData(data);
    
     return observable({
        records,
        get selected() {// 当records发生改变时候,自动计算被选中的数据。
                return this.records.filter((item: { selection: boolean; }) => item.selection);
        },
        unSelectAll,
        selectAll,
        select,
        unSelect,
     });
}
​
​
//ant表格封装使用
 this.ListDs = DataSet(ds);
 const {selected}= this.ListDs;
 const rowSelection = {
            onSelect: (record, selected, selectedRow) => {//控制选中或者取消选中
                record.selection = !record.selection;
            },
            onSelectAll: (selected, selectedRows, changeRows) => {
                changeRows.map(record => record.selection = selected);//控制全选
            },
            columnWidth: '60px',//选择列宽度
            fixed: false,
 };
<Table
 rowSelection={
 {
                selectedRowKeys: selected.map(record => record.__key),
                fixed: true,
                type: 'checkbox',
                ...rowSelection,
 }}
/>

(3)实现表格删除功能

给每一条record提供一个__status属性,用来记录当前record的数据状态,包含(add:新增,update: 数据更新,delete删除),同时提供一个dirty属性,判断数据有没有变化过。

表格删除功能,主要是调用DS.deleteAll方法,传参要删除的records

const {confirm} = Modal;
// selectedRecords: 想要删除的记录。
function deleteAll(selectedRecords: any[]) {
        confirm({
            content: '确认删除选中行?',
            okText: '确认',
            cancelText: '取消',
            onOk: () => {
                // 删除已经存在的数据。然后重新查询。首先过滤调新增状态的数据
                const deleteData = selectedRecords.filter(item => item.__status !== RecordStatus.add).map(item => item.toData());
                
                //取消全部勾选
                this.unSelectAll(); 
                
                //transport.destroyed写删除配置,destroyed用来存储删除状态的record。
                if (transport.destroyed && this.destroyed.length) {
                    const param = transport.destroyed({dataSet, data: deleteData});
                    
                    //调用删除接口
                    deleted(param.url, param.data).then(res => {
                        if (res && !res.failed) {
                            notification.success({
                                message: '操作成功',
                                placement: 'bottomRight',
                            })
                            this.query();//删除完自动重新查询
                        }
                    }).catch(() => {
                    })
                }
            },
        })
    }

5.封装代码

(1)DataSet方法代码

import {observable, toJS} from "mobx";
import {RecordStatus} from './enum';
import {deleted, get as httpGet, post, put} from '../utils/http';
import {Modal, notification} from 'antd';
​
const {confirm} = Modal;
const QueryDataSet = (dataSet: any) => {
    const queryDataSet = {
        ...dataSet,
        fields: dataSet.queryFields,
        queryFields: [],
        transport: {},
        autoQuery: false,
        data: [{}],
    }
    return DataSet(queryDataSet, true);
}
const dataSet = (dataSet: { data?: any[]; fields?: any[]; queryFields?: any[]; transport?: any; autoQuery?: boolean; pageSize?: number, selection?: any, event?: any }, isQuery?: boolean) => {
​
    const {
        data = [],
        fields = [],
        transport,
        autoQuery = false,
        pageSize = 10,
        selection = 'multiple',
        event
    } = dataSet;
    let records: any[] = []; // 记录数据集
    let queryBar = dataSet.queryFields?.length;
    let originalData: any[] = []; //源数据
    let loading = false; //异步加载
    let queryDataSet = {}; // 查询dataSet
    let number = 1; // 当前页为第几页
    let size = pageSize; // 每页数据条数
    let totalElements = data.length || 0; // 总共多少数据
    let totalPages = data.length / size || 0; // 总共多少页
    // 初始化
    const init = (allData: any[]) => {
        //判断是否是已经初始化查询dataSet
        if (!isQuery) {
            queryDataSet = QueryDataSet(dataSet);
        }
        originalData = allData;
        records = toObserveArrayData(allData);
    }
​
    // 查询方法
    function query(currentIndex?:any,currentSize?:any) {
        if (transport.read) {
            const queryData = this.queryDataSet.current.toData();
            const param = transport.read({dataSet: this, data: queryData});
            this.loading = true;
            httpGet(param.url, {
                ...param.data,
                page: currentIndex || this.number,
                pageSize: currentSize || this.size
            }).then(res => {
                this.loading = false;
                if (res && !res.failed) {
                    console.log(res.data);
                    this.originalData = res.data;
                    this.records = toObserveArrayData(res.data);
                    this.totalElements = res.totalElements || 0;
                    this.number = res.number || 0;
                    this.totalPages = res.totalPages || 0;
                    this.size = res.size || 10;
                }
            })
        }
    }
​
    // 将普通数据转化为可观察的记录数据
    const toObserveArrayData = (arrayData: any[]) => {
        return arrayData.map(item => {
            const defaultValues: { [x: string]: any; } = {};
            fields.map(field => {
                if (field.defaultValue !== null && field.defaultValue !== undefined
                ) {
                    defaultValues[field.name] = field.defaultValue;
                }
            })
            return observable({
                data: {
                    ...defaultValues,
                    ...item,
                },
                __status: RecordStatus.sync,
                __key: Math.random(),
                selection: false,
                editor: false,
                get validateStatus() {
                    const a = fields.filter(field => this.validate(field).status === 'error');
                    return a.length <= 0;
                },
                validate(field: any) {
                    if (field.required && (this.get(field.name) === null || this.get(field.name) === undefined || this.get(field.name) === '')) {
                        return {
                            status: 'error',
                            type: 'required',
                            message: `请输入${field.label}!`,
                        }
                    }
                    if ((field.min || field.min === 0) && field.type === 'Number' && (this.get(field.name) === 0 || this.get(field.name))) {
                        if (this.get(field.name) < field.min) {
                            return {
                                status: 'error',
                                type: 'min',
                                message: `${field.label}的值必须大于${field.min}!`,
                            }
                        }
                    }
                    if ((field.max || field.max === 0) && field.type === 'Number' && (this.get(field.name) === 0 || this.get(field.name))) {
                        if (this.get(field.name) > field.max) {
                            return {
                                status: 'error',
                                type: 'max',
                                message: `${field.label}的值必须小于${field.max}!`,
                            }
                        }
                    }
                    if (field.validate && field.validate({value: this.get(f
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

is微凉

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值