搞轮子: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>
  );
};
默认为都未选择,当点击后改变状态。
总结
这一些告一段落,已经没什么好讲的了