比如我们有一行卡片,中间需要用分割线隔开,让视觉更美观。
如果卡片数量固定且较少,很简单,将所有卡片一一写出,中间加上分隔线即可。实际情况卡片数可能较多,一一写出代码量线性增长不好维护,而且很可能数量不定,那就要换种思路了。
🗺️ 解法 1:map + index > 0 去除分割线
条件判断 index > 0 使得开头没有分割线。
const ListWithSeparator: React.FC<IProps> = ({ list }) => {
return (
<div style={{ display: 'flex', gap: 40, flexWrap: 'wrap', }}>
{list.map((item, index) => (
// 🔥 1. React.Fragment 包裹多个组件
<React.Fragment key={item.id}>
{/* 🔥 2. 通过 index 判断使得开头没有分割线 */}
{index > 0 && (
<Divider type="vertical" style={{ margin: 0, alignSelf: 'center' }} />
)}
<Component data={item}> 组件 </Component>
</React.Fragment>
))}
</div>
);
};
🔂 解法 2:forEach/for/reduce + index > 0 去除分割线
和解法 1 没本质区别,不展开说。
💉 解法 3:forEach + pop 去除分割线
将多余的分割线放到尾部,然后通过 pop 去除。
const ListWithSeparator: React.FC<IProps> = ({ list }) => {
const renderList = () => {
const result = []
list.forEach(item => {
result.push(<Component data={item}> 组件 </Component>)
result.push(<Divider type="vertical" style={{ margin: 0, alignSelf: 'center' }} />)
})
// 去除尾部多余的分隔组件
result.pop()
return result
}
return (
<div style={{ display: 'flex', gap: 40, flexWrap: 'wrap' }}>
{renderList()}
</div>
);
};
当然两次 push 也可以写成一次,效果等同:
result.push(
<Component data={item}> 组件 </Component>,
<Divider type="vertical" style={{ margin: 0, alignSelf: 'center' }} />
)
⚖️ 对比
map + index:代码量少,但需要React.Fragment包裹以及设置key。forEach + pop:可读性好,代码层面简单没有多余的视觉干扰。性能更好,因为每次遍历无需判断index(小数组无需关心性能问题)。
🧠 泛化
事情不在于多,而在于好。
每次做完一个需求,我们都要思考下,既然模式固定,能否泛化:
在一系列组件间插入分割线抽象成在一系列组件间插入组件。
实现的话要么组件化要么封装函数。本场景是封装函数。
封装好处是让“小心翼翼移除多余分隔组件”的逻辑被黑盒,调用者无需感知和“担惊受怕”。
我们可以从 array.prorotype.join 汲取灵感,也设计一个类似的函数:
join(Components, Separator): Component[]
不一样的是,我们返回的是数组而非字符串,这样才能被渲染。
两版都给出。
foreEach + pop:
import React from 'react';
type Element = React.ReactElement;
export const join = (Components: Element[], Separator: Element): Element[] => {
const result: Element[] = [];
Components.forEach(component => {
result.push(component);
result.push(Separator);
});
// 去除尾部多余的分隔组件
result.pop();
return result;
};
map + index < 0的解法也给出下:
import React from 'react';
type Element = React.ReactElement;
export const join = (Components: Element[], Separator: Element): Element[] => {
return Components.map((component, index) => (
<React.Fragment key={index}>
{index > 0 && Separator}
{component}
</React.Fragment>
));
};
使用就非常简单了:
{join(
// 组件列表
list.map(item) => <Component data={item} />,
// 分割线
<Divider type="vertical" style={{ margin: 0, alignSelf: 'center' }} />
)}