刚到公司,组长铁蛋屁颠屁颠过来了:光哥,今天搞个需求。给项目中所有新建和编辑页的“提交”和“暂存”按钮边上补个“关闭”按钮。同时对之前的这些提交按钮,加个逻辑,在成功执行完之前的提交请求后,在控制台打印一个‘我爱996’。 需求不多,就涉及几十个页面,下班之前能搞定吧? 我:好的领导(你妹的)。
琢磨了一下最好能封个公共组件,直接把之前的按钮包进去,然后巴巴拉拉全套整齐了,多好。
思路:
- 要接受并展示原页面按钮
- 新增一个按钮
- 保持原理按钮的点击事件,并且要接受到原请求成功后的一个通知
实现:
- 传入页面按钮
<HOCBtn>
<Button type="primary" onClick={() => onFinish(0)}>
暂存
</Button>
<Button type="primary" onClick={() => onFinish(1)}>
提交
</Button>
</HOCBtn>
- 通过props.children接受到,对其进行遍历,获取到每个子元素然后筛选clone出“Button”,增加点击事件,当新事件点击的时候,调用他原来的点击事件,并拿到返回值作为参数,根据这个值在恰当的时间输出‘我爱996’
import React, { useEffect } from "react";
import { Button } from "antd";
const HOCBtn = ({ children }) => {
// 监听子元素点击,当有一个返回true,并执行方法
const handleOnClickChild = async (peddlingFlag) => {
let flag = await peddlingFlag;
if (flag) {
console.log('我爱996')
}
};
// 渲染子元素,插入自定义方法
const renderChildren = (child) => {
if (child.type === Button) {
return React.cloneElement(child, {
onClick: () => {
handleOnClickChild(child.props.onClick());
},
});
} else {
return child;
}
};
const onCancel = () => {
};
return (
<div className="foot-btn">
<Button onClick={onCancel}>关闭</Button>
{React.Children.map(children, renderChildren)}
</div>
);
};
export default HOCBtn;
铁蛋:搞的不错嘛。 看了下时间还有半小时下班,接着说:光哥,我这还有个小需求。各个列表页的Table操作项里面的操作内容太多了,操能不能把操作项分个类,除原'查看'和'编辑'保留单独操作项,其余的都丢到DropDown里去。大概几十个页面。
思路:
- 对原来所有的按钮根据名称,进行分类
- 查看 和 编辑拎出来不变,其余项进行整合
实现:
- 使用
<DropdownMenu>
<a onClick={}>{translateTitle("取消询价")}</a>
<a onClick={}>{translateTitle("分配询价员")}</a>
<a onClick={}>{translateTitle("查看")}</a>
</DropdownMenu>
- 处理
const WHITE_LIST = [translateTitle("查看"), translateTitle("编辑")]; // 白名单
const DropdownMenu = (props: Props) => {
const { children } = props;
// 获取所有的 a 标签
const aTags = React.Children.toArray(children);
// 过滤出不等于查看的 a 标签
const filteredTags = aTags.filter((tag: any) => !WHITE_LIST.includes(tag.props.children));
//固定的a标签
const fixedTags = aTags.filter((tag: any) => WHITE_LIST.includes(tag.props.children));
let dropDownList = React.createElement(
"div",
{
className: "drop-down ant-dropdown-menu",
},
filteredTags
);
// 把过滤后的 a 标签放进 dropdown 组件中,让用户做出选择
return (
<div className="action-self">
{fixedTags}
{filteredTags?.length ? (
<Dropdown overlay={dropDownList}>
<span className="more-action">{translateTitle("操作")}</span>
</Dropdown>
) : null}
</div>
);
};
知识点解析:
- React.Children.map: 用于遍历 React 组件的子元素。它有两个参数:第一个参数是组件的子元素 prop,第二个参数是一个函数,它将被调用,并以每个子元素作为参数。该函数应该返回一个修改后的子元素版本,它将用于新数组中由 map 方法返回的结果。
- React.cloneElement: 用于克隆并返回一个新的 React 元素,同时可以为该元素指定新的 props。它接收两个参数:第一个参数是要克隆的 React 元素,第二个参数是一个可选的对象,用于指定新的 props。克隆后的元素将保留原始元素的 key 和 ref,同时也继承了新的 props。该方法通常用于在不改变原始元素的情况下,修改元素的 props 或增加新的 props。
- React.createElement: 用于创建并返回一个新的虚拟 DOM 元素。它接受三个参数:元素的类型(可以是表示 HTML 标签的字符串或表示 React 组件的函数)、包含要分配给元素的任何属性的对象,以及元素的任何子元素。通常在 JSX 中使用此函数来创建和渲染 React 组件。
- React.Children.toArray: 用于将 props.children 转换为一个扁平的数组。它接收一个 props.children 作为参数,并返回一个数组,其中包含所有的子元素(包括字符串、数字、元素或者数组)。
有时候直接对 props.children 进行操作,可能会出现一些问题:
首先,当 props.children 只有一个子元素时,它的类型不是数组,而是一个单独的 React 元素。如果直接对它进行数组操作,会导致类型错误。此外,当 props.children 没有子元素时,它的值为 null,而不是一个空数组,这也可能导致类型错误或者其他问题。
另外,当 props.children 包含嵌套的子元素时,如果不对它进行递归处理,可能会无法访问到所有的子元素,或者无法正确处理子元素的嵌套结构。 因此,为了避免这些问题,建议在对 props.children 进行操作之前,先使用 React.Children.toArray 将其转换为一个扁平的数组,这样可以方便地遍历和处理所有的子元素,无论它们的类型和数量。