• antd源码分析之——折叠面板(collapse)


    官方文档 https://ant.design/components/collapse-cn/

    目录

    一、antd中的collapse

      代码目录

      1、组件结构图(♦♦♦重要)

      2、源码节选:antd/components/collapse/collapse.tsx

      3、源码节选:antd/components/collapse/CollapsePanel.tsx

     二、RcCollapse

      代码目录

      1、组件内部属性结构及方法调用关系图(♦♦♦重要

      2、组件应用的设计模式(♦♦♦重要

      3、源码节选:rc-collapse/Collapse.jsx

      4、源码节选:rc-collapse/panel.jsx

      

    一、antd中的collapse

    antd组件中有些使用了React 底层基础组件(查看具体列表点这里),collapse就是这种类型的组件

    antd中collapse主要源码及组成结构如下,其中红色标注的Rc开头的组件是React底层基础组件

     代码目录

    1、组件结构图:

     

    2、antd/components/collapse/collapse.tsx

    export default class Collapse extends React.Component<CollapseProps, any> {
      static Panel = CollapsePanel;
    
      static defaultProps = {
        prefixCls: 'ant-collapse',
        bordered: true,
        openAnimation: { ...animation, appear() { } },
      };
    
      renderExpandIcon = () => {
        return (
          <Icon type="right" className={`arrow`} />
        );
      }
    
      render() {
        const { prefixCls, className = '', bordered } = this.props;
        const collapseClassName = classNames({
          [`${prefixCls}-borderless`]: !bordered,
        }, className);
        return (
          <RcCollapse
            {...this.props}
            className={collapseClassName}
            expandIcon={this.renderExpandIcon}
          />
        );
      }
    }

    3、antd/components/collapse/CollapsePanel.tsx

    export default class CollapsePanel extends React.Component<CollapsePanelProps, {}> {
      render() {
        const { prefixCls, className = '', showArrow = true } = this.props;
        const collapsePanelClassName = classNames({
          [`${prefixCls}-no-arrow`]: !showArrow,
        }, className);
        return <RcCollapse.Panel {...this.props} className={collapsePanelClassName} />;
      }
    }

     二、RcCollapse

    由上述Collapse源码不难看出,折叠面板组件的实现逻辑主要在RcCollapse中,下面是核心代码、组件内部属性结构及方法调用关系图

    代码目录

     1、组件内部属性结构及方法调用关系图

    2、组件应用的设计模式

    这个组件中主要使用里“聪明组件和傻瓜组件”模式、“组合组件”模式

    a、聪明组件和傻瓜组件:

      遵循职责分离原则,把获取和管理数据的逻辑放在父组件,作为聪明组件;把渲染界面的逻辑放在子组件,也就是傻瓜组件

      聪明组件:Collapse,负责获取和管理数据

      • getItems(),获取数据,将props中的数据传递给子组件CollapsePanel;
      • onClickItem(),管理数据,计算active的值传递给子组件;

      傻瓜组件:Panel,只负责渲染;

      • 根据父组件传入的数据控制渲染逻辑,如active时的渲染效果

    b、组合组件:

      适用场景:Collapse组件中Collapse是一个容器,包含一个或多个CollapsePanel,可以有一个(手风琴)或多个Panel展开(active),展开的样式不同与未展开

      一般实现:每个Panel中传入isActive状态和onclick方法,在Panel内部实现渲染逻辑

      缺陷:每个Panel中要写多个props参数

           每个Panel中处理onclick的相同逻辑,重复代码,增加Panel成本高

           Collapse中控制active逻辑在每次新增Panel时也要修改

      组合组件模式:借助React.Children.map或React.cloneElement使列表中多个子组件的公共处理移到父组件中统一处理

      Collapse中的实现:Collapse渲染时调用this.getItems(),在this.getItems()中使用React.Children.map配置panel的onItemClick事件和activeKey等其他属性

                 Panel只在点击事件时调用父组件中定义的onItemClick,没有冗余代码,降低了增加Panel的成本

    PS:组件设计模式详细内容可以自行查找相关资料,推荐掘金小册《React 实战:设计模式和最佳实践》,本文部分内容摘自该文

    3、rc-collapse/Collapse.jsx

    class Collapse extends Component {
      constructor(props) {
        super(props);
        ……this.state = {
          ……
        };
      }
    
      componentWillReceiveProps(nextProps) {
    …… } onClickItem(key) { …… } getItems() { const activeKey = this.state.activeKey; const { prefixCls, accordion, destroyInactivePanel, expandIcon } = this.props; const newChildren = []; Children.forEach(this.props.children, (child, index) => { if (!child) return; // If there is no key provide, use the panel order as default key const key = child.key || String(index); const { header, headerClass, disabled } = child.props; let isActive = false; if (accordion) { isActive = activeKey[0] === key; } else { isActive = activeKey.indexOf(key) > -1; } const props = { …… openAnimation: this.state.openAnimation, accordion, children: child.props.children, onItemClick: disabled ? null : () => this.onClickItem(key), expandIcon, }; newChildren.push(React.cloneElement(child, props)); }); return newChildren; } setActiveKey(activeKey) { if (!('activeKey' in this.props)) { this.setState({ activeKey }); } this.props.onChange(this.props.accordion ? activeKey[0] : activeKey); } render() { const { prefixCls, className, style, accordion } = this.props; const collapseClassName = classNames({ [prefixCls]: true, [className]: !!className, }); return ( <div className={collapseClassName} style={style} role={accordion ? 'tablist' : null}> {this.getItems()} </div> ); } }

    4、rc-collapse/panel.jsx

    class CollapsePanel extends Component {
      handleItemClick = () => {
        if (this.props.onItemClick) {
          this.props.onItemClick();
        }
      }
    
      handleKeyPress = (e) => {
        if (e.key === 'Enter' || e.keyCode === 13 || e.which === 13) {
          this.handleItemClick();
        }
      }
    
      render() {
        const {
          ……
        } = this.props;
        const headerCls = classNames(`${prefixCls}-header`, {
          [headerClass]: headerClass,
        });
        const itemCls = classNames({
          [`${prefixCls}-item`]: true,
          [`${prefixCls}-item-active`]: isActive,
          [`${prefixCls}-item-disabled`]: disabled,
        }, className);
    
        let icon = null;
        if (showArrow && typeof expandIcon === 'function') {
          icon = React.createElement(expandIcon, { ...this.props });
        }
        return (
          <div className={itemCls} style={style} id={id}>
            <div
              className={headerCls}
              onClick={this.handleItemClick}
              role={accordion ? 'tab' : 'button'}
              tabIndex={disabled ? -1 : 0}
              aria-expanded={`${isActive}`}
              onKeyPress={this.handleKeyPress}
            >
              {showArrow && (icon || <i className="arrow" />)}
              {header}
            </div>
            <Animate
              showProp="isActive"
              exclusive
              component=""
              animation={this.props.openAnimation}
            >
              <PanelContent
                prefixCls={prefixCls}
                isActive={isActive}
                destroyInactivePanel={destroyInactivePanel}
                forceRender={forceRender}
                role={accordion ? 'tabpanel' : null}
              >
                {children}
              </PanelContent>
            </Animate>
          </div>
        );
      }
    }
  • 相关阅读:
    三地气温的典型相关分析
    android 使用 BroadcastReceiver 总结
    发布网站问题笔记
    Android 两个Activity 实现数据的来回传递
    input 标签屏蔽谷歌浏览器高亮显示
    javascript 获取当前html 标签的位置
    c# 四舍五入
    利用SQL的charindex实现字符串数组和Split函数
    ExtJs 一些常用的控件
    C#中return、break、continue的用法
  • 原文地址:https://www.cnblogs.com/zs-note/p/11322468.html
Copyright © 2020-2023  润新知