搞轮子:SortBar排序栏的升级
前言
在此之前,曾经写过两篇关于组件嵌套关系的组件文章:
Tabs 组件首次使用 React.Children.map 来”解构“子组件,释放子组件写法
<Tabs value={value} swipeable>
<Tabs.Panel title="标签1">内容 1</Tabs.Panel>
<Tabs.Panel title="标签2">内容 2</Tabs.Panel>
<Tabs.Panel title="标签3">内容 3</Tabs.Panel>
<Tabs.Panel title="标签4">内容 4</Tabs.Panel>
</Tabs>
TabBar 组件再次基础上,又使用了 React.cloneElement,使组件的状态变化由父组件提供
<TabBar
activeKey={activeKey}
onChange={(key: any) => {
setActiveKey(key);
}}
>
<TabBar.Item itemKey="home" title="主页" icon={<IconTabbarHome />} />
<TabBar.Item
itemKey="financial"
title="理财"
icon={<IconTabbarFinancial />}
/>
<TabBar.Item itemKey="user" title="我的" icon={<IconTabbarUser />} />
</TabBar>
TabBar 是底部的菜单栏,有选中和非选择两种状态,所以要给父组件设置”当前选中项“,每个子组件设置唯一标识符,这样才能判断那个元素被选中了
而今天说的 SortBar 状态变为三种,分别为未选择,降序、升序
正文
我们要做成什么样子呢?如下图所示:
有筛选、和三个有状态的子组件。其实和 TabBar 组件很像,无非是多了筛选组件,把两种状态改为三种
先看书写结构:
<SortBar
activeKey={activeKey}
onChange={(key: string, status: string) => {
setActiveKey(key);
}}
onClick={() => {
console.log("点击筛选");
}}
>
<SortBar.Item title="年化" itemKey="annualized" />
<SortBar.Item title="期限" itemKey="term" />
<SortBar.Item title="价格" itemKey="price" />
</SortBar>
和 TabBar 可以说一摸一样,其中 onClick
是针对点击筛选组件的,其核心思路和 TabBar一样,核心代码是:
const items = React.Children.map(children, (element, index) => {
if (!React.isValidElement(element)) return null;
return cloneElement(element, {
key: index,
title: element.props.title,
itemKey: element.props.itemKey || index,
selected: activeKey === element.props.itemKey,
onClick: (status: string) => onHandleClick(element.props.itemKey, status),
});
});
React.Children 遍历子元素
React.isValidElement 判断子元素是否是 React 元素
cloneElement 在原来的子组件上添加其他元素
- selected:新添加的元素,判断当前选中key和自身的 key 是否是同一个
- onClick:点击的时候哪个key,什么status(状态)传给父元素,好让父元素做业务处理
选中和未选中此子组件由父组件控制,而选中此子组件的状态则在子组件中完成,请看代码:
const Item: FC<SortBarItemProps> = (props) => {
const { title, selected, onClick } = props;
const [status, setStatus] = useState("0");
useEffect(() => {
if (selected === false) {
setStatus("0");
}
}, [selected]);
const onHandleClick = () => {
if (status === "0" || status === "2") {
setStatus("1");
onClick && onClick("1");
} else if (status === "1") {
setStatus("2");
onClick && onClick("2");
}
};
return (
<div className={`${prefixCls}`} onClick={onHandleClick}>
{title}
{status === "0" && <IconFilterEmty size="sm" />}
{status === "1" && <IconFilterDown size="sm" />}
{status === "2" && <IconFilterUp size="sm" />}
</div>
);
};
默认为都未选择,当点击后改变状态。
总结
这一些告一段落,已经没什么好讲的了