在中后台场景中,查询表格的场景占比较高。你会发现该场景写起代码会充斥着重复性的代码,比如点击下一页的时候,你需要把表单查询条件带过去,点击查询的时候你需要把页码归为 1 等常规逻辑。当然还有很多增强功能,比如排序、filter 、多选功能。
我们面临“高效”和“灵活”权衡问题,高效的话可以一个组件来实现上面功能然后通过 props 来配置,这样子可能会减少了灵活性,灵活又可能需要使用方手写很多代码才能完成一个功能,失去了高效性。
最后借鉴了 Egg 和 Babel 的思想,通过 Core + Plugin 的方式来平衡“高效”和“灵活”。同时设计的过程中,我们减少新概念出现,尽量使用业界通用的方案来减少学习成本。我们花费大量时间去思考插件设计,并且数字供应链已经做第一个吃螃蟹的人,基本上所有的查询表格场景都用这个方案了。现在是时间放出来让大家 Review 下了,期望有更多场景的输入,这样子 useTable 才能做得更好。
useTable 针对的是查询表格场景的一些状态逻辑处理,比如请求的时候有 loading,请求成功之后设置 dataSource,点击下一页的时候请求等。本身不关心 View 层,并不是重新造一个 Table 组件。
它包含以下特性:
整体概览:
最底层 useTable 具备插件能力,可以通过插件生成具备插件能力的 Hook,可以一直套娃下去,然后适配不同 Design 系统。上层建设可以多种多样,底层可以共用一套插件技术体系。
基于前面所遇到的场景和思考,我们发布了 ahooksjs useTable v0.1.0 版本让大家可以尝鲜。
useTable 提供核心 useTable & useFormTable 规范,还有对应 next 版本的 useNextTable & useNextFormTable,方便使用 Fusion Next 的开发者快速使用,后续也会支持 Antd,还提供官方多个场景插件。下面一一介绍下:
上面说了这么多,是时候看看真面目了,是骡子是马拉出来遛遛。下面主要是一些伪代码,主要为了简单描述下如何使用,更多可以看官网的快速开始。
useNextFormTable 会返回不同组件的 Props,然后可以赋给对应的组件,useNextFormTable 帮你管理状态逻辑,现在看起来并没有什么特别之处。
const Component = () => {
const { formProps, tableProps, paginationProps } = useNextFormTable(list);
return (
<div>
<SchemaForm {...formProps} components={{ Input }} style={{ marginBottom: 20 }} inline>
<Field name="name" title="name" x-component={'Input'} />
<FormButtonGroup>
<Submit>查询</Submit>
<Reset>重置</Reset>
</FormButtonGroup>
</SchemaForm>
<Table {...tableProps}>
<Table.Column title="name" dataIndex="name" width={200} />
</Table>
<Pagination style={{ marginTop: 16 }} {...paginationProps} />
</div>
);
};
上面只是完成一个最 Basic 的能力,那如果需要加上其他功能比如多选,我们如何实现呢?我们提供的方案是 useNextFormTable 具备扩展能力,然后根据场景沉淀出不同插件,并且这些插件可以同时使用。具体可以看下下面的例子是如何使用的?
加上多选的功能,多选的插件已经帮你完成下面的功能了
import "@alifd/next/dist/next.css";
import React from "react";
import useNextFormTable from "@ahooksjs/next-form-table";
import useSelectionPlugin from "@ahooksjs/use-selection-plugin"
import { Table, Pagination } from "@alifd/next";
import { SchemaForm, Field, Submit, Reset, FormButtonGroup } from "@formily/next";
import { Input } from "@formily/next-components";
export default function App() {
const plugin = useSelectionPlugin()
const { formProps, tableProps, paginationProps } = useNextFormTable(list, {
plugins: [plugin]
});
return (
<div style={{ padding: 20 }}>
<SchemaForm
{...formProps}
components={{ Input }}
style={{ marginBottom: 20 }}
inline
>
<Field name="name" title="name" x-component={"Input"} />
<FormButtonGroup>
<Submit>查询</Submit>
<Reset>重置</Reset>
</FormButtonGroup>
</SchemaForm>
<Table {...tableProps}>
<Table.Column title="name" dataIndex="name" width={200} />
</Table>
<Pagination style={{ marginTop: 16 }} {...paginationProps} />
</div>
);
}
基本上你只需要引入多选插件,然后直接使用它就可以完成多选功能,而不需要在各个地方写逻辑。当然多插件一起使用也是支持的,文档上有对应的例子。
如果官方插件不能满足你的要求的,你完全可以自定义自己的插件,还有如果你部门也有自己的 Design 系统,组件的 props 定义不一样,可以看文档的『插件开发』和『结合 Design』,帮助你快速定制符合你具备插件化能力的查询表格。
上层建设可以多种多样,你可以用源码开发,也可以通过数据驱动,还可以通过配置化的方式,底层共用同一套插件体系。useTable 不关心上层建设,只是提供方便上层建设的能力。
ahooks useTable 现在还是很年轻,需要更多场景的输入才能越做越好。大家们有任何问题或者建议都可以提到 Github Issue 上,我们会第一时间处理。
1
otakustay 2020-08-26 10:16:00 +08:00 2
我认为在 hook 上这么玩 plugin 的方案非常扯谈,hook 本来就是一套函数式的设计,基于原子的数据来增强就好
const tableProps = useTable(list); const propsWithSelection = useSelection(tableProps); const propsWithSort = useSort(propsWithSelection); 这样甚至可以很快速地构建出适合自己的 hook: const useOwnTable = compose(useTable, useSelection, useSort); 任何一个东西,加上插件就是在增加概念,插件不是一个公认的逻辑,每一个插件系统的实现和设计都是不一样的,让人去理解成本并不低 |
2
monkindey OP @otakustay plugin 其实就是一个 hook,只是一个高级 hook,返回对应的属性而已。而且多选、排序都是依赖查询流程的,如果你只是关心 props 的话,没办法一下就完成一个功能,比如多选勾选的时候,重新点击查询会清空掉勾选的值,排序也是一样的情况。
其实 useTable 是具备插件合并的能力,每一个插件也是一个 hook,你可以理解 useTable 就是一个 compose,useTable 、useSelction 、useSort 是一个插件。也就是你的理解: const useOwnTable = compose(useTable, useSelection, useSort); 相当于 const useOwnTable = useTable(useSelection, useSort) |
3
monkindey OP @otakustay 插件你可以理解是增加一个概念,但是里面用的东西都不是新概念,都是一些业界流行的概念合并在一起。因为我们要写一个地方就可以解决一个功能的,不需要我加一个多选的功能或者排序的功能都要用 useState 状态管理,然后设置 props,然后在请求的链路中还要去清除勾选项,你会发现代码写起来就很繁琐,特别是当你负责很多类似页面的时候。
|
4
otakustay 2020-08-26 11:16:23 +08:00
> 而且多选、排序都是依赖查询流程的,如果你只是关心 props 的话,没办法一下就完成一个功能
我会选择先弄一个 useTableParams 的东西来组装参数,useTableXxxParams 追加参数 算是设计理念上的不同吧,我比较喜欢“如无必要,勿增概念”的玩法 |
5
monkindey OP @otakustay 额额,同意你的设计理念,我也喜欢简单。我也思考很久,说服很多人,整一个设计并没有加概念,插件其实一个 hook,里面有 middleware 还有 compose 都是业界通用方案,只是在这里把它们合在一起。
组装参数只是一部分,还有在查询前做事情,在查询后做事情,比如我们这边有人实现一个服务端驱动的插件,还可以拦截 response 做格式化。 因为我不知道你们有没有做过中台页面,可能代入感比较弱。你提的问题我们这边在对这个方案的时候也提出来过,但是最后都认同了。如果你想进一步交流的话,可以加我微信 atob('bW9ua2luZGV5') ( console 执行下 ) |