搞轮子:SortBar排序栏的升级

前言

在此之前,曾经写过两篇关于组件嵌套关系的组件文章:

搞轮子:从业务到组件Tabs

搞轮子:TabBar标签栏的自救

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 状态变为三种,分别为未选择,降序、升序

正文

我们要做成什么样子呢?如下图所示:

image-20211223091530386

有筛选、和三个有状态的子组件。其实和 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>
  );
};

默认为都未选择,当点击后改变状态。

总结

这一些告一段落,已经没什么好讲的了