自定义排序组件,实现了与Form表单的完美集成,支持升序、降序以及不排序三种状态的灵活切换。即提高了表单的交互性,又在现有UI框架基础上扩展自定义功能,满足特定业务需求。
需求:要在 Form 表单中实现排序组件功能,类似 Table 中的排序组件,没有现成的组件,只能自己写。
1. Form 表单结合排序组件
(1)Form
表单逻辑:
tsx
// 创建 Form 实例,用于管理所有数据状态
const [form] = Form.useForm();
// 定义排序逻辑
const [subjectOrder, setSubjectOrder] = useState("none");
// 字段值更新时触发回调事件
const onValuesChange = (values: any, allValues: any) => {
console.log("form: ", values, allValues);
};
<Form form={form} onValuesChange={onValuesChange} layout="inline">
<Form.Item name="subjectOrder" valuePropName="checked">
<Checkbox
onChange={(e) => setSubjectOrder(e.target.checked ? "asc" : "desc")}
>
<CustomSorter label="学科" type={subjectOrder} />
</Checkbox>
</Form.Item>
</Form>;
注意:Form.Item 中使用 Checkbox 组件时,需要配置valuePropName="checked"
,不然会提示警告信息:
Warning: [antd: Checkbox] value
is not a valid prop, do you mean checked
?
官网也有说明:valuePropName
为子节点的值的属性。Switch、Checkbox 的 valuePropName 应该是 checked,否则无法获取这个两个组件的值。该属性为 getValueProps 的封装,自定义 getValueProps 后会失效
具体可查看官网demo:Form表单
(2)自定义 CustomSorter
组件逻辑:
tsx
import { CaretUpOutlined, CaretDownOutlined } from "@ant-design/icons";
import "./index.scss";
interface iProps {
label: string;
type: string; // none-不排序;asc-升序;desc-降序
}
const CustomSorter = (props: iProps) => {
const { label, type = "none" } = props;
return (
<div className="custom-sorter">
<div className="label">{label}</div>
<div className="sorter">
<CaretUpOutlined className={type === "asc" ? "active" : ""} />
<CaretDownOutlined className={type === "desc" ? "active" : ""} />
</div>
</div>
);
};
export default CustomSorter;
最初的想法是通过Checkbox
组件的状态控制升序、降序,然后再隐藏多选框,就能实现业务需求了。
效果如下图所示:
缺点:只能控制两种状态切换(asc-升序;desc-降序),无法切换回原始状态(none不排序状态),而且封装不彻底,Checkbox
无法放到 CustomSorter
里面去,移到里面去之后,Checkbox
就不受 Form 表单控制了,还需要调整代码才行,导致状态管理复杂,搞得更麻烦了。最终决定自己实现,不借助 Checkbox
了。
2. 自定义排序组件
要想实现三种状态的切换,需要自定义排序组件。而且还要和 Form 表单联动,即受控于 Form 表单。
自定义或第三方的表单控件,也可以与 Form 组件一起使用。只要该组件遵循以下的约定:
- 提供受控属性
value
或其它与valuePropName
的值同名的属性。 - 提供
onChange
事件或trigger
的值同名的事件。
自定义 CustomSorter
组件逻辑,如下所示:
tsx
interface iProps {
label: string; // 标签文本
value?: string; // 当前排序状态值 none-不排序;asc-升序;desc-降序
onChange?: (value: string) => void; // 排序状态变更回调
}
const CustomSorter = (props: iProps) => {
const { label, value = "none", onChange } = props;
const [order, setOrder] = useState(value);
const handleChange = () => {
// 通过三元运算符精炼地实现状态循环切换逻辑
const val = order === "none" ? "asc" : order === "asc" ? "desc" : "none";
setOrder(val);
if (onChange) {
onChange(val);
}
};
return (
<div className="custom-sorter" onClick={handleChange}>
<div className="label">{label}</div>
<div className="sorter">
<CaretUpOutlined className={order === "asc" ? "active" : ""} />
<CaretDownOutlined className={order === "desc" ? "active" : ""} />
</div>
</div>
);
};
export default CustomSorter;
样式文件 index.scss 如下所示:
scss
.custom-sorter {
display: inline-flex;
align-items: center;
cursor: pointer;
.sorter {
display: flex;
flex-direction: column;
color: #ddd;
.active {
color: #1d52a9;
}
.anticon {
margin-left: 5px;
font-size: 12px;
position: relative;
}
.anticon-caret-up {
top: 2px;
}
.anticon-caret-down {
top: -2px;
}
}
}
最终实现效果,如下所示: