前言

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

搞轮子:从业务到组件Tabs

搞轮子:TabBar标签栏的自救

Tabs 组件首次使用 React.Children.map 来”解构“子组件,释放子组件写法

1
2
3
4
5
6
<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,使组件的状态变化由父组件提供

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<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 组件很像,无非是多了筛选组件把两种状态改为三种

先看书写结构:

1
2
3
4
5
6
7
8
9
10
11
12
13
 <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一样,核心代码是:

1
2
3
4
5
6
7
8
9
10
11
12
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(状态)传给父元素,好让父元素做业务处理

选中和未选中此子组件由父组件控制,而选中此子组件的状态则在子组件中完成,请看代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
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>
);
};

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

总结

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