下拉菜单组件是一个可以将页面上比较冗杂的操作收纳在一个点,以便节省页面空间,达到整洁美观的目的。
Antd的下拉菜单组件中有一个点,就是他的内部元素必须是Antd的Menu组件,感觉有点捆绑的意思。
这里留一个小问题,为什么触发方式是一个数组,而不是单个的
- export interface DropDownProps {
- trigger ? :('click' | 'hover')[]; // 触发方式
- overlay: React.ReactNode; // 下拉菜单所承载的内容元素,要求为Antd的Menu组件
- style ? :React.CSSProperties; // 行内样式
- onVisibleChange ? :(visible ? :boolean) = >void; // 监听下拉菜单出现/消失
- visible ? :boolean; // 菜单是否显示
- disabled ? :boolean; // 菜单是否可以用
- align ? :Object; // 这个参数目前没有被使用
- getPopupContainer ? :(triggerNode: Element) = >htmlElement; // 渲染的挂载点,默认为body
- prefixCls ? :string; // 样式类的命名前缀
- className ? :string; // 样式
- placement ? :'topLeft' | 'topCenter' | 'topRight' | 'bottomLeft' | 'bottomCenter' | 'bottomRight';
- // 弹出框与触发点的对齐方式
- }
因为这个组件主要使用的是rc-dropdown组件
所以这里只是对其参数做了一些封装,比较简单。
- export
- default class Dropdown extends React.Component < DropDownProps,
- any > {
- static Button: typeof DropdownButton;
- static defaultProps = {
- prefixCls: 'ant-dropdown',
- mouseEnterDelay: 0.15,
- mouseLeaveDelay: 0.1,
- placement: 'bottomLeft',
- };
- // 设定一个动画效果名称
- getTransitionName() {
- const {
- placement = ''
- } = this.props;
- // js的indexOf()可以使用在Array上也可以使用在String上
- // 使用方法一样,第一个参数是匹配的对象,第二个参数是从哪里开始匹配
- if (placement.indexOf('top') >= 0) {
- return 'slide-down';
- }
- return 'slide-up';
- }
- componentDidMount() {
- // 这里就在检测菜单内容是否是antd的menu组件,并且检测menu组件的样式
- const {
- overlay
- } = this.props;
- const overlayProps = (overlay as any).props as any;
- // warning函数还是和之前学习的一样的用法
- warning(!overlayProps.mode || overlayProps.mode === 'vertical', `mode = "${overlayProps.mode}"is not supported
- for Dropdown\'s Menu.`,
- );
- }
- render() {
- const { children, prefixCls, overlay, trigger, disabled } = this.props;
- // 将dropdown包裹的触发器加以封装,再渲染
- const dropdownTrigger = cloneElement(children as any, {
- className: classNames((children as any).props.className, `${prefixCls}-trigger`),
- disabled,
- });
- // menu cannot be selectable in dropdown defaultly
- const overlayProps = overlay && (overlay as any).props;
- const selectable = (overlayProps && 'selectable ' in overlayProps)
- ? overlayProps.selectable : false;
- // 同样的将dropdown包裹的内容加以封装,再渲染
- const fixedModeOverlay = cloneElement(overlay as any, {
- mode: 'vertical ',
- selectable,
- });
- return (
- // 最后将所有参数传入rc-dropdown组件
- <RcDropdown
- {...this.props}
- transitionName={this.getTransitionName()}
- trigger={disabled ? [] : trigger}
- overlay={fixedModeOverlay}
- >
- {dropdownTrigger}
- </RcDropdown>
- );
- }
- }'
这个组件是一个带按钮的下拉菜单组件,其实其原理就是使用的之前所讲的ButtonGroup来进行组合的一个组件。
想要了解ButtonGroup组件的可以点击这里
这个组件的props继承了两个其他组件的props,这是typescript的interface的一个特性,可以继承多个,来形成一个新的。
- export interface DropdownButtonProps extends ButtonGroupProps, DropDownProps {
- type?: 'primary' | 'ghost' | 'dashed'; // 按钮类型
- disabled?: boolean; // 是否禁用
- onClick?: React.MouseEventHandler<any>; // 点击事件
- children?: any; // 子节点
- }
从render函数就可以看出这个组件就是antd为了方便,为大家封装好了的一个带下拉菜单的按钮组件
- render() {
- const {
- type, disabled, onClick, children,
- prefixCls, className, overlay, trigger, align,
- visible, onVisibleChange, placement, getPopupContainer,
- ...restProps,
- } = this.props;
- const dropdownProps = {
- align,
- overlay,
- trigger: disabled ? [] : trigger,
- onVisibleChange,
- placement,
- getPopupContainer,
- };
- if ('visible' in this.props) {
- (dropdownProps as any).visible = visible;
- }
- return (
- <ButtonGroup
- {...restProps}
- className={classNames(prefixCls, className)}
- >
- <Button
- type={type}
- disabled={disabled}
- onClick={onClick}
- >
- {children}
- </Button>
- <Dropdown {...dropdownProps}>
- <
- Button
- type
- =
- {type}
- disabled
- =
- {disabled}
- >
- <Icon type="down" />
- </Button>
- </Dropdown>
- </ButtonGroup>
- );
- }
写到这里就完了么?是滴,这一节就完了,因为查看了一下rc-dropdown的实现,然后根据平常看代码的习惯
从render()函数入口发现这一段代码
- render() {
- const {
- prefixCls, children,
- transitionName, animation,
- align, placement, getPopupContainer,
- showAction, hideAction,
- overlayClassName, overlayStyle,
- trigger, ...otherProps,
- } = this.props;
- return (
- <Trigger
- {...otherProps}
- prefixCls={prefixCls}
- ref={this.saveTrigger}
- popupClassName={overlayClassName}
- popupStyle={overlayStyle}
- builtinPlacements={placements}
- action={trigger}
- showAction={showAction}
- hideAction={hideAction}
- popupPlacement={placement}
- popupAlign={align}
- popupTransitionName={transitionName}
- popupAnimation={animation}
- popupVisible={this.state.visible}
- afterPopupVisibleChange={this.afterVisibleChange}
- popup={this.getMenuElement()}
- onPopupVisibleChange={this.onVisibleChange}
- getPopupContainer={getPopupContainer}
- >
- {children}
- </Trigger>
- );
- }
然后再看到
这个组件,居然是另外一个库rc-trigger
- Trigger
的实现之后,才知道原来tigger组件才是下拉菜单的核心。
- rc-trigger
所以还没完呢,只是需要讲解的太多,不适合在一篇文章中讲全面,所以敬请期待下一篇文章,
我们将会学习rc-trigger组件的实现,之后会再写一篇rc-dropdown的组件实现解读,
最后看完这三篇文章,再倒过来重温一遍,你将会学到怎么样一层层的包装组件,将一个基础组件包装成为一个高级组件的过程。
来源: https://juejin.im/post/59f064ad5188252c224d356d