记录一次代码迭代的思路
功能主体是一个筛选表单,筛选项基本都是Select,筛选项间有联动,使用antd组件
表单部分封装了一个Form外壳以及查询重置等功能,没什么好说的,筛选项部分因为赶工期堆了屎,直接做成了这样
直接使用Select
怎么笨怎么写的,问题一目了然
- pageSize 999
- 每个使用的地方全都有请求接口的代码
// 代码辣眼睛,无法搜索,每个需要筛选的地方都需要cv
使用封装后的SearchInput
使用示例代码中的SearchInput稍微改造了一下作为Field,规避了pageSize===999的问题 将接口请求放进了具体的FilterItem内,能去掉大片的重复代码
但是代码仍然有些冗余,比如筛选的联动只是在外部监听onChange然后清空后置选项,能否在FormItem内就能实现这种联动呢?
const lackDepend = React.useMemo(
() => depend.find((name) => !params[name]),
[depend, params]
);
const searchAction = useCallback(
(search = '') => {
// ...
},
[params, lackDepend]
);
return (
<Form.Item label='所属项目' name={name} {...rest}>
<SearchInput
placeholder={
lackDepend ? `请先选择${dependLabels[lackDepend]}` : '请选择'
}
action={searchAction}
disabled={!!lackDepend}
{...searchProps}
/>
</Form.Item>
);
当前表单内数据联动
问题的关键就是FormItem内能否获取到其他FormItem的数据。两个方案: FormItem的 children render props 参数是 FormInstance useFormInstance hook
再次封装了相同逻辑后,具体的FilterItem逻辑变得更为简单
const searchAction = (search = '', params: any) => {
// 访问接口,处理数据
// TODO searchAction 的代码是否可以继续封装呢
return fetchQueryProjects(...);
};
const ProjectFilter: React.FC<ComposeFilterProps> = ({
name = 'projectId',
...rest
}) => {
return (
<FilterItem label='所属项目' name={name} action={searchAction} {...rest} />
);
};
render props需要是一个纯函数,无法在这一层比较params,只能传入SearchInput内比较
<Form.Item dependencies={dependencies} noStyle>
{(form) => {
const params = form.getFieldsValue(dependencies);
const lackDepend =
dependNotNull && dependencies.find((name) => !params[name]);
return (
<Form.Item name={name} {...rest}>
{/** ... */}
</Form.Item>
);
}}
</Form.Item>
改用hook的获取FormInstance,并且将表单内的联动逻辑尽可能放在组件内
const form = Form.useFormInstance();
// TODO deepCompare?
const coordinateReset = useMemo(
() => coordinate.reduce((pre, key) => ({ ...pre, [key]: undefined }), {}),
[coordinate]
);
/** 是否可以同步更新label onchange不应该更新name */
const syncLabel = labelName && name !== labelName;
/** 是否可以同步更新value onchange不应该更新name */
const syncValue = valueName && name !== valueName;
return (
<Form.Item dependencies={dependencies} noStyle>
{() => {
const params = form.getFieldsValue(dependencies);
const lackDepend =
dependNotNull && dependencies.find((name) => !params[name]);
return (
<Form.Item name={name} {...rest}>
<SearchInput
disabled={!!lackDepend}
action={action}
defaultOptions={defaultOptions}
placeholder={
lackDepend ? `请先选择${dependLabels[lackDepend]}` : '请选择'
}
params={params}
{...searchProps}
onChange={(newValue, option: any) => {
form.setFieldsValue({
...coordinateReset,
...(syncLabel && { [labelName]: option.label }),
...(syncValue && { [valueName]: option.value }),
});
searchProps?.onChange?.(newValue, option);
}}
/>
</Form.Item>
);
}}
</Form.Item>
);
TODO
ComposeFilter或FilterItem这一层能否避免无效的rerender?是否可以用key来解决? 使用高阶组件来封装而不是普通组件?