官方文档 https://ant.design/components...
目录
一、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 {
static Panel = CollapsePanel;
static defaultProps = {
prefixCls: 'ant-collapse',
bordered: true,
openAnimation: { ...animation, appear() { } },
};
renderExpandIcon = () => {
return (
);
}
render() {
const { prefixCls, className = '', bordered } = this.props;
const collapseClassName = classNames({
[`${prefixCls}-borderless`]: !bordered,
}, className);
return (
);
}
}
3、antd/components/collapse/CollapsePanel.tsx
export default class CollapsePanel extends React.Component {
render() {
const { prefixCls, className = '', showArrow = true } = this.props;
const collapsePanelClassName = classNames({
[`${prefixCls}-no-arrow`]: !showArrow,
}, className);
return ;
}
}
二、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 (
{this.getItems()}
);
}
}
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 (
{showArrow && (icon || )}
{header}
{children}
);
}
}