From fae49e6e36f64030b4754db01c17f6bc4cb55b21 Mon Sep 17 00:00:00 2001 From: hengkx Date: Mon, 18 May 2020 09:39:40 +0800 Subject: [PATCH 1/3] refactor(alert): rewrite with hook and support strict mode --- components/alert/index.tsx | 221 ++++++++++++++++++------------------- 1 file changed, 106 insertions(+), 115 deletions(-) diff --git a/components/alert/index.tsx b/components/alert/index.tsx index eabe1d76e302..8b27121b8541 100755 --- a/components/alert/index.tsx +++ b/components/alert/index.tsx @@ -1,5 +1,4 @@ import * as React from 'react'; -import * as ReactDOM from 'react-dom'; import CloseOutlined from '@ant-design/icons/CloseOutlined'; import CheckCircleOutlined from '@ant-design/icons/CheckCircleOutlined'; import ExclamationCircleOutlined from '@ant-design/icons/ExclamationCircleOutlined'; @@ -12,7 +11,7 @@ import CloseCircleFilled from '@ant-design/icons/CloseCircleFilled'; import Animate from 'rc-animate'; import classNames from 'classnames'; -import { ConfigConsumer, ConfigConsumerProps } from '../config-provider'; +import { ConfigContext } from '../config-provider'; import getDataOrAriaProps from '../_util/getDataOrAriaProps'; import ErrorBoundary from './ErrorBoundary'; import { replaceElement } from '../_util/reactNode'; @@ -48,11 +47,6 @@ export interface AlertProps { onClick?: React.MouseEventHandler; } -export interface AlertState { - closing: boolean; - closed: boolean; -} - const iconMapFilled = { success: CheckCircleFilled, info: InfoCircleFilled, @@ -67,66 +61,65 @@ const iconMapOutlined = { warning: ExclamationCircleOutlined, }; -export default class Alert extends React.Component { - static ErrorBoundary = ErrorBoundary; +interface AlertInterface extends React.FC { + ErrorBoundary: typeof ErrorBoundary; +} - state = { - closing: false, - closed: false, - }; +const Alert: AlertInterface = props => { + const [closing, setClosing] = React.useState(false); + const [closed, setClosed] = React.useState(false); - handleClose = (e: React.MouseEvent) => { + const ref = React.createRef(); + const { getPrefixCls, direction } = React.useContext(ConfigContext); + + const handleClose = (e: React.MouseEvent) => { e.preventDefault(); - const dom = ReactDOM.findDOMNode(this) as HTMLElement; + const dom = ref.current as HTMLElement; dom.style.height = `${dom.offsetHeight}px`; // Magic code // 重复一次后才能正确设置 height dom.style.height = `${dom.offsetHeight}px`; - this.setState({ - closing: true, - }); - this.props.onClose?.(e); + setClosing(true); + props.onClose?.(e); }; - animationEnd = () => { - this.setState({ - closing: false, - closed: true, - }); - this.props.afterClose?.(); + const animationEnd = () => { + setClosing(false); + setClosed(true); + props.afterClose?.(); }; - getShowIcon() { - const { banner, showIcon } = this.props; + const getShowIcon = () => { + const { banner, showIcon } = props; // banner 模式默认有 Icon return banner && showIcon === undefined ? true : showIcon; - } + }; - getType() { - const { banner, type } = this.props; + const getType = () => { + const { banner, type } = props; if (type !== undefined) { return type; } // banner 模式默认为警告 return banner ? 'warning' : 'info'; - } + }; - getClosable() { - const { closable, closeText } = this.props; + const getClosable = () => { + const { closable, closeText } = props; // closeable when closeText is assigned return closeText ? true : closable; - } + }; - getIconType() { - const { description } = this.props; + const getIconType = () => { + const { description } = props; // use outline icon in alert with description - return (description ? iconMapOutlined : iconMapFilled)[this.getType()] || null; - } + return (description ? iconMapOutlined : iconMapFilled)[getType()] || null; + }; - renderIconNode({ prefixCls }: { prefixCls: string }) { - const { icon } = this.props; - const iconType = this.getIconType(); + const renderIconNode = ({ prefixCls }: { prefixCls: string }) => { + const { icon } = props; + const iconType = getIconType(); if (icon) { return replaceElement(icon, {icon}, () => ({ className: classNames(`${prefixCls}-icon`, { @@ -135,14 +128,14 @@ export default class Alert extends React.Component { })); } return React.createElement(iconType, { className: `${prefixCls}-icon` }); - } + }; - renderCloseIcon({ prefixCls }: { prefixCls: string }) { - const { closeText } = this.props; - return this.getClosable() ? ( + const renderCloseIcon = ({ prefixCls }: { prefixCls: string }) => { + const { closeText } = props; + return getClosable() ? ( ) : null; - } - - renderAlert = ({ getPrefixCls, direction }: ConfigConsumerProps) => { - const { - description, - prefixCls: customizePrefixCls, - message, - banner, - className = '', - style, - onMouseEnter, - onMouseLeave, - onClick, - } = this.props; - const { closing, closed } = this.state; - - const prefixCls = getPrefixCls('alert', customizePrefixCls); - - const isShowIcon = this.getShowIcon(); - const type = this.getType(); - const closable = this.getClosable(); - - const alertCls = classNames( - prefixCls, - `${prefixCls}-${type}`, - { - [`${prefixCls}-closing`]: closing, - [`${prefixCls}-with-description`]: !!description, - [`${prefixCls}-no-icon`]: !isShowIcon, - [`${prefixCls}-banner`]: !!banner, - [`${prefixCls}-closable`]: closable, - [`${prefixCls}-rtl`]: direction === 'rtl', - }, - className, - ); - - const closeIcon = this.renderCloseIcon({ prefixCls }); - - const dataOrAriaProps = getDataOrAriaProps(this.props); - - const iconNode = this.renderIconNode({ prefixCls }); - - return closed ? null : ( - -
- {isShowIcon ? iconNode : null} - {message} - {description} - {closeIcon} -
-
- ); }; - render() { - return {this.renderAlert}; - } -} + const { + description, + prefixCls: customizePrefixCls, + message, + banner, + className = '', + style, + onMouseEnter, + onMouseLeave, + onClick, + } = props; + + const prefixCls = getPrefixCls('alert', customizePrefixCls); + + const isShowIcon = getShowIcon(); + const type = getType(); + const closable = getClosable(); + + const alertCls = classNames( + prefixCls, + `${prefixCls}-${type}`, + { + [`${prefixCls}-closing`]: closing, + [`${prefixCls}-with-description`]: !!description, + [`${prefixCls}-no-icon`]: !isShowIcon, + [`${prefixCls}-banner`]: !!banner, + [`${prefixCls}-closable`]: closable, + [`${prefixCls}-rtl`]: direction === 'rtl', + }, + className, + ); + + const closeIcon = renderCloseIcon({ prefixCls }); + + const dataOrAriaProps = getDataOrAriaProps(props); + + const iconNode = renderIconNode({ prefixCls }); + + return closed ? null : ( + +
+ {isShowIcon ? iconNode : null} + {message} + {description} + {closeIcon} +
+
+ ); +}; + +Alert.ErrorBoundary = ErrorBoundary; + +export default Alert; From 427ab7188bff1b6457bbd004003f15213fb847c9 Mon Sep 17 00:00:00 2001 From: hengkx Date: Mon, 18 May 2020 19:03:17 +0800 Subject: [PATCH 2/3] Update index.tsx --- components/alert/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/alert/index.tsx b/components/alert/index.tsx index 8b27121b8541..6c89fc7f8048 100755 --- a/components/alert/index.tsx +++ b/components/alert/index.tsx @@ -69,7 +69,7 @@ const Alert: AlertInterface = props => { const [closing, setClosing] = React.useState(false); const [closed, setClosed] = React.useState(false); - const ref = React.createRef(); + const ref = React.useRef(); const { getPrefixCls, direction } = React.useContext(ConfigContext); const handleClose = (e: React.MouseEvent) => { From b39e5f1d17943377a669a20ce49bc39c08f90cb1 Mon Sep 17 00:00:00 2001 From: hengkx Date: Tue, 19 May 2020 14:03:16 +0800 Subject: [PATCH 3/3] improve code --- components/alert/index.tsx | 78 +++++++++++++++----------------------- 1 file changed, 30 insertions(+), 48 deletions(-) diff --git a/components/alert/index.tsx b/components/alert/index.tsx index 6c89fc7f8048..e9149cd14dab 100755 --- a/components/alert/index.tsx +++ b/components/alert/index.tsx @@ -65,12 +65,27 @@ interface AlertInterface extends React.FC { ErrorBoundary: typeof ErrorBoundary; } -const Alert: AlertInterface = props => { +const Alert: AlertInterface = ({ + description, + prefixCls: customizePrefixCls, + message, + banner, + className = '', + style, + onMouseEnter, + onMouseLeave, + onClick, + showIcon, + closable, + closeText, + ...props +}) => { const [closing, setClosing] = React.useState(false); const [closed, setClosed] = React.useState(false); const ref = React.useRef(); const { getPrefixCls, direction } = React.useContext(ConfigContext); + const prefixCls = getPrefixCls('alert', customizePrefixCls); const handleClose = (e: React.MouseEvent) => { e.preventDefault(); @@ -90,14 +105,8 @@ const Alert: AlertInterface = props => { props.afterClose?.(); }; - const getShowIcon = () => { - const { banner, showIcon } = props; - // banner 模式默认有 Icon - return banner && showIcon === undefined ? true : showIcon; - }; - const getType = () => { - const { banner, type } = props; + const { type } = props; if (type !== undefined) { return type; } @@ -105,21 +114,14 @@ const Alert: AlertInterface = props => { return banner ? 'warning' : 'info'; }; - const getClosable = () => { - const { closable, closeText } = props; - // closeable when closeText is assigned - return closeText ? true : closable; - }; - - const getIconType = () => { - const { description } = props; - // use outline icon in alert with description - return (description ? iconMapOutlined : iconMapFilled)[getType()] || null; - }; + // closeable when closeText is assigned + const isClosable = closeText ? true : closable; + const type = getType(); - const renderIconNode = ({ prefixCls }: { prefixCls: string }) => { + const renderIconNode = () => { const { icon } = props; - const iconType = getIconType(); + // use outline icon in alert with description + const iconType = (description ? iconMapOutlined : iconMapFilled)[type] || null; if (icon) { return replaceElement(icon, {icon}, () => ({ className: classNames(`${prefixCls}-icon`, { @@ -130,9 +132,8 @@ const Alert: AlertInterface = props => { return React.createElement(iconType, { className: `${prefixCls}-icon` }); }; - const renderCloseIcon = ({ prefixCls }: { prefixCls: string }) => { - const { closeText } = props; - return getClosable() ? ( + const renderCloseIcon = () => { + return isClosable ? (