从复杂到简单:BaseTablePresenter让前端表格操作更加轻松
在上篇文章中我们讲了如何实现一个下拉刷新,上拉加载,带有搜索功能的列表状态管理器
今天讲一个管理后台常用的功能,表格功能
需求分析
首先看看下面的表格页面,有筛选,分页,渲染等功能
根据这些功能我们可以定义出页面视图需要的数据模型
视图数据模型
首先是分页相关的数据
interface Pagination {
current: number;
total: number;
pageSize: number;
}
- data: 每一行的数据
- params: 请求参数
- loading: ...
export interface TableState<Row = any> {
loading: boolean;
data: Row[];
params: Record<any, any>;
pagination: Pagination & Record<string, any>;
[p: string]: any;
}
有了这些数据就可以满足一个表格的正常渲染了
视图操作方法
接下来定义一些表格需要的操作方法
loading控制
这里我们用两个方法来控制loading的切换
showLoading() {
if (this._loadingCount === 0) {
this.setState((s) => {
s.loading = true;
});
}
this._loadingCount += 1;
}
hideLoading() {
this._loadingCount -= 1;
if (this._loadingCount === 0) {
this.setState((s) => {
s.loading = false;
});
}
}
fetchData
获取数据的方法,这里我们定义一个异步函数,声明好接口定义,函数内容直接抛错,需要等具体使用在去实现真正的fetchData
async fetchData(
params: Partial<TableState['params']> & {
current: number;
pageSize: number;
},
): Promise<{
data: any[];
current: number;
pageSize: number;
total: number;
}> {
throw Error('请实现fetchTable');
}
updateData
updateData方法用来调用fetchData,更新数据,切换loading
/**
* 发请求,更新数据
* @returns
*/
updateData() {
const params: Record<any, any> = {};
Object.entries(this.state.params || {}).forEach(([k, v]) => {
if (v !== undefined) {
Object.assign(params, { [k]: v });
}
});
this.showLoading();
return this.fetchData({
current: this.state.pagination.current || 1,
pageSize: this.state.pagination.pageSize || 10,
...params,
})
.then((res) => {
this.setState((s) => {
s.pagination.current = res.current;
s.pagination.pageSize = res.pageSize;
s.pagination.total = res.total;
s.data = res.data;
});
return res;
})
.finally(() => {
this.hideLoading();
});
}
setPagination
setPagination,顾名思义就是更新分页状态的方法,这里有一个潜在的逻辑,当pageSize修改的时候,分页要切换为第一页
/**
* 更新参数,如果修改的参数是不是current,重置current为1
* @param pagination
*/
setPagination(pagination: Partial<Pagination>) {
this.setState((s) => {
let current = pagination.current || s.pagination.current;
if (pagination?.pageSize) {
if (pagination.pageSize !== s.pagination.pageSize) {
current = 1;
}
}
s.pagination = {
...s.pagination,
...pagination,
current,
};
});
}
参数设置
下面是更改参数和重置参数的方法
/**
* 更新参数,重置当前请求页面为1
* @param params
*/
setParams(params: Partial<TableState['params']>) {
const d: Partial<TableState['params']> = {};
Object.entries(params).forEach(([k, v]) => {
if (v !== undefined) {
Object.assign(d, {
[k]: v,
});
}
});
this.setState((s) => {
s.params = {
...s.params,
...d,
};
s.pagination.current = 1;
});
}
resetParams() {
this.setState((s) => {
s.params = {} as Record<any, any>;
});
}
分页状态修改
很多时候我们都是用回调函数去监听ui组件的分页状态修改,这里也提供一个对应的方法
/**
* 切换页面,并且更新数据
* @param p
*/
onPageChange(p: Pagination) {
this.setPagination(p);
return this.updateData();
}
完整的代码例子
完整的代码例子可以查看github仓库,记得给个star哈
如何使用
npm install @clean-js/pro-presenters
import React from 'react';
import { usePresenter } from '@clean-js/react-presenter';
import { BaseTablePresenter } from '@clean-js/pro-presenters';
export const demo = () => {
const { presenter, state } = usePresenter(BaseTablePresenter);
return (
<Table
loading={state.loading}
columns={state.columns}
pagination={state.pagination}
dataSource={state.data}
onChange={(p) => {
presenter.onPageChange?.(p);
}}
/>
);
};
当我们有多个表格要使用的时候,初始化多个presenter即可 下面的两个usePresenter会返回不同的实例
import React from 'react';
import { usePresenter } from '@clean-js/react-presenter';
import { BaseTablePresenter } from '@clean-js/pro-presenters';
export const demo = () => {
const { presenter, state } = usePresenter(BaseTablePresenter);
const { p: tableP } = usePresenter(BaseTablePresenter);
return (
<div>
<Table
loading={state.loading}
columns={state.columns}
pagination={state.pagination}
dataSource={state.data}
onChange={(p) => {
presenter.onPageChange?.(p);
}}
/>
<Table
loading={tableP.state.loading}
columns={tableP.state.columns}
pagination={tableP.state.pagination}
dataSource={tableP.state.data}
onChange={(p) => {
tableP.onPageChange?.(p);
}}
/>
</div>
);
};
如何拓展功能
假设我们需要添加一个columns属性,用来配置UI,我们可以继承这个BaseTablePresenter,对其进行扩展即可
type Columns = { key: string }[];
class CustomTableP extends BaseTablePresenter<
{ name: string },
{ columns: Columns }
> {
constructor() {
super({
data: [],
loading: false,
params: {},
pagination: {
current: 1,
pageSize: 10,
total: 1,
},
columns: [],
});
}
setColumns(columns: Columns) {
this.setState((s) => {
s.columns = columns;
});
}
test() {
return 'test';
}
}
export const demo = () => {
const { presenter, state } = usePresenter(CustomTableP);
return (
<Table
loading={state.loading}
columns={state.columns}
pagination={state.pagination}
dataSource={state.data}
onChange={(p) => {
presenter.onPageChange?.(p);
}}
/>
);
};
转载自:https://juejin.cn/post/7202888010035757111