From 724cd44d693f5eb2a456960bc5db3d80b0210c40 Mon Sep 17 00:00:00 2001 From: Dreamacro <305009791@qq.com> Date: Tue, 2 Jul 2019 23:22:19 +0800 Subject: [PATCH] Migration: Message & Modal to hooks component --- src/components/Message/index.tsx | 119 ++++++++++++++----------------- src/components/Modal/index.tsx | 109 ++++++++++++---------------- src/components/Tags/index.tsx | 4 +- 3 files changed, 103 insertions(+), 129 deletions(-) diff --git a/src/components/Message/index.tsx b/src/components/Message/index.tsx index 5833888..19be416 100644 --- a/src/components/Message/index.tsx +++ b/src/components/Message/index.tsx @@ -1,10 +1,10 @@ -import * as React from 'react' -import { Icon } from '@components' +import React, { useState, useLayoutEffect } from 'react' import classnames from 'classnames' import { unmountComponentAtNode, render } from 'react-dom' +import { Icon } from '@components' +import { noop } from '@lib/helper' import './style.scss' -const noop = () => {} const TYPE_ICON_MAP = { info: 'info', success: 'check', @@ -30,74 +30,63 @@ interface MessageProps { removeComponent: typeof noop } -export class Message extends React.Component { +export function Message (props: MessageProps) { + const { + removeComponent = noop, + onClose = noop, + icon = , + content = '', + type = 'info', + duration = 1500 + } = props - /** - * static function to call Message directly - */ - static info = ( - content: string, - duration?: number, - onClose?: typeof noop - ) => showMessage({ type: 'info', content, duration, onClose }) + const [visible, setVisible] = useState(false) - static success = ( - content: string, - duration?: number, - onClose?: typeof noop - ) => showMessage({ type: 'success', content, duration, onClose }) + useLayoutEffect(() => { + window.setTimeout(() => setVisible(true), 0) - static warning = ( - content: string, - duration?: number, - onClose?: typeof noop - ) => showMessage({ type: 'warning', content, duration, onClose }) + const id = window.setTimeout(() => { + setVisible(false) + onClose() + }, duration) + return () => window.clearTimeout(id) + }, []) - static error = ( - content: string, - duration?: number, - onClose?: typeof noop - ) => showMessage({ type: 'error', content, duration, onClose }) - - static defaultProps: MessageProps = { - content: '', - type: 'info', - icon: , - duration: 1500, - onClose: noop, - removeComponent: noop - } - - state = { - visible: false - } - - componentDidMount () { - // TODO: optimize animation - // fix do not show animation when element mounted - setTimeout(() => this.setState({ visible: true }), 0) - - setTimeout(() => { - this.setState({ visible: false }) - this.props.onClose() - }, this.props.duration) - } - - render () { - const { removeComponent, icon, content, type } = this.props - - return ( -
!this.state.visible && removeComponent()} - > - {icon} - {content} -
- ) - } + return ( +
!visible && removeComponent()} + > + {icon} + {content} +
+ ) } +export const info = ( + content: string, + duration?: number, + onClose?: typeof noop +) => showMessage({ type: 'info', content, duration, onClose }) + +export const success = ( + content: string, + duration?: number, + onClose?: typeof noop +) => showMessage({ type: 'success', content, duration, onClose }) + +export const warning = ( + content: string, + duration?: number, + onClose?: typeof noop +) => showMessage({ type: 'warning', content, duration, onClose }) + +export const error = ( + content: string, + duration?: number, + onClose?: typeof noop +) => showMessage({ type: 'error', content, duration, onClose }) + export function showMessage (args: ArgsProps) { // create container element const container = document.createElement('div') diff --git a/src/components/Modal/index.tsx b/src/components/Modal/index.tsx index 88bbb8e..d210321 100644 --- a/src/components/Modal/index.tsx +++ b/src/components/Modal/index.tsx @@ -1,12 +1,11 @@ -import * as React from 'react' +import React, { useRef, useLayoutEffect } from 'react' import classnames from 'classnames' import { createPortal } from 'react-dom' import { BaseComponentProps } from '@models' import { Button } from '@components' +import { noop } from '@lib/helper' import './style.scss' -const noop = () => {} - interface ModalProps extends BaseComponentProps { // show modal show?: boolean @@ -33,75 +32,61 @@ interface ModalProps extends BaseComponentProps { onClose?: typeof noop } -export class Modal extends React.Component { +export function Modal (props: ModalProps) { + const { + show = true, + title = 'Modal', + size = 'small', + footer= true, + onOk = noop, + onClose = noop, + bodyClassName, + bodyStyle, + className, + style, + children + } = props - static defaultProps: ModalProps = { - show: true, - title: 'Modal', - size: 'small', - footer: true, - onOk: noop, - onClose: noop - } + const portalRef = useRef(document.createElement('div')) + const maskRef = useRef() - // portal container - $container: Element + useLayoutEffect(() => { + document.body.appendChild(portalRef.current) + return () => document.body.removeChild(portalRef.current) + }, []) - $modal = React.createRef() - - $mask = React.createRef() - - constructor (props) { - super(props) - - // create container element - const container = document.createElement('div') - document.body.appendChild(container) - this.$container = container - } - - componentWillUnmount () { - document.body.removeChild(this.$container) - } - - private handleMaskClick = (e) => { - const { onClose } = this.props - - if (e.target === this.$mask) { + function handleMaskClick (e) { + if (e.target === maskRef.current) { onClose() } } - render () { - const { show, size, title, footer, children, className, bodyClassName, style, bodyStyle, onOk, onClose } = this.props - const modal = ( + const modal = ( +
+
{title}
-
{title}
-
{children}
- { - footer && ( -
- - -
- ) - } -
+ className={classnames('modal-body', bodyClassName)} + style={bodyStyle} + >{children}
+ { + footer && ( +
+ + +
+ ) + }
- ) + + ) - return createPortal(modal, this.$container) - } + return createPortal(modal, portalRef.current) } diff --git a/src/components/Tags/index.tsx b/src/components/Tags/index.tsx index a727d1a..22f029b 100644 --- a/src/components/Tags/index.tsx +++ b/src/components/Tags/index.tsx @@ -1,4 +1,4 @@ -import React, { useState, useRef, useEffect } from 'react' +import React, { useState, useRef, useLayoutEffect } from 'react' import { useTranslation } from 'react-i18next' import { BaseComponentProps, I18nProps } from '@models' import { noop } from '@lib/helper' @@ -21,7 +21,7 @@ export function Tags (props: TagsProps) { const [showExtend, setShowExtend] = useState(false) const ulRef = useRef() - useEffect(() => { + useLayoutEffect(() => { setShowExtend(ulRef.current.offsetHeight > 30) }, [])