美文网首页
Dialog组件

Dialog组件

作者: sweetBoy_9126 | 来源:发表于2019-11-12 17:25 被阅读0次

确定api

我们的dialog是否隐藏是用户可以直接在外面设置的,所以这个visible需要作为一个props传入,而不是组件内部的状态

  • dialog/dialog.tsx
import React from 'react'

interface Props {
  visible: boolean
}
const Dialog: React.FunctionComponent<Props> = (props) => {
  return (
    props.visible ?
      <div>dialog</div> :
      null
  )
}
export default Dialog
  • dialog/dialog.example.tsx
import React, {useState} from 'react';
import Dialog from './dialog';

export default function () {
  const [x, setX] = useState(false);
  return (
    <div>
      <button onClick={() => setX(!x)}>click</button>
      <Dialog visible={x}/>
    </div>
  );
}

用户自定义传入button

这里我们把buttons参数作为一个数组,数组里面的每一个元素都是一个ReactElement元素

  • dialog.tsx
interface Props {
+  buttons: ReactElement[]
}

<footer className={sc('footer')}>
      {props.buttons}
</footer>

如果用户想要关闭弹窗需要再自己传入的按钮里单独调用关闭方法

  • dialog.example.tsx
<Dialog visible={x} title="你好啊" buttons={
  [
    <Button type="primary" size="mini" onClick={() => setX(false)>ok</Button>,
    <Button size="mini" onClick={() => setX(false)}>ok</Button>
  ]
}>
  小改改!
</Dialog>

接受closeOnClickMask参数,来设置可否点击遮罩层关闭弹窗,默认为false

interface Props {
  closeOnClickMask?: boolean;
}
<div className={sc('mask')} onClick={(e) => {
          if (props.closeOnClickMask) {
            props.onClose(e)
          }
}}/>
Dialog.defaultProps = {
  closeOnClickMask: false
}

传送门(createPortal)

如果我们的组件里用户在外面手动设置了z-index那么我们的弹窗就有可能覆盖不了它
解决方法:把我们的组件元素放到body里,然后把它的z-index尽量设置小一点让用户可以修改

import ReactDOM from 'react-dom'

const Dialog: React.FunctionComponent<Props> = (props) => {
  const x = props.visible ?
    <Fragment>
      <div className={sc('mask')} onClick={(e) => {
        if (props.closeOnClickMask) {
          props.onClose(e)
        }
      }}/>
      <div className={sc()}>
        <div className={sc('close')}>
          <Icon name="close" size="mini" onClick={props.onClose}/>
        </div>
        <header className={sc('header')}>
          {props.title ? props.title : '提示'}
        </header>
        <main className={sc('main')}>
          {props.children}
        </main>
        <footer className={sc('footer')}>
          {props.buttons.map((button, index) => {
            return React.cloneElement(button, {key: index})
          })}
        </footer>
      </div>
    </Fragment>
    :
    null
  return (
    // 将x添加到body里
    ReactDOM.createPortal(x, document.body)
  )
}

实现一个alert

实现:我们可以直接通过alert的方法显示我们的dialog组件
思路:声明一个alert方法,导出它,需要我们动态创建组件(本来组件不存在,用到的时候才存在)

const alert = (content: string) => {
  const component = <Dialog visible={true} onClose={() => {
    // 关闭的时候重新克隆一个componet组件,并且把克隆的这个visble设置为false,挂载到div上
    ReactDOM.render(React.cloneElement(component, {visible: false}), div)
    // 把div从页面上卸载
    ReactDOM.unmountComponentAtNode(div)
    // 删除div
    div.remove()
  }} title="提示">{content}</Dialog>
  const div = document.createElement('div')
  document.body.append(div)
  ReactDOM.render(component, div)
}
export {alert};

使用

import {alert} from './dialog'
<Button onClick={() => alert('1') }>提示</Button>

实现confirm和modal

  1. confirm
const confirm = (content: string, yes?: () => void, no?: () => void) => {
  const onYes = () => {
    yes && yes()
    ReactDOM.render(React.cloneElement(component, {visible: false}), div)
    ReactDOM.unmountComponentAtNode(div)
    div.remove()
  }
  const onNo = () => {
    no && no()
    ReactDOM.render(React.cloneElement(component, {visible: false}), div)
    ReactDOM.unmountComponentAtNode(div)
    div.remove()
  }
  const component = (
    <Dialog onClose={() => {}} visible={true}
            buttons={
              [
                <Button onClick={onYes}>ok</Button>,
                <Button onClick={onNo}>cancel</Button>
              ]
            }
    >
      {content}
    </Dialog>
  )
  const div = document.createElement('div')
  document.body.append(div)
  ReactDOM.render(component, div)
}

使用

<Button onClick={() => confirm('1',() => {console.log('success')}, () => {console.log('fail')}) }>提示</Button>
  1. modal
const modal = (content: ReactNode | ReactFragment) => {
  const onClose = () => {
    ReactDOM.render(React.cloneElement(component, {visible: false}), div)
    ReactDOM.unmountComponentAtNode(div)
    div.remove()
  }
  const component = <Dialog onClose={onClose} visible={true}>
    {content}
  </Dialog>
  const div = document.createElement('div')
  ReactDOM.render(component, div)
}

使用

<Button onClick={() => modal(<h1>小改改</h1>) }>modal</Button>

问题:我们的modal是无法提供按钮的,而且dialog是被封装在modal里面的,显示是通过visible的render来实现的,如果用户自己写关闭按钮的话,我们无法在外部关闭dialog,也就相当于我们要在一个函数外部改函数内部的变量
解决方法:modal里返回一个可以关闭dialog的方法

const modal = (content: ReactNode | ReactFragment) => {
+  return onClose
}

使用

const openModal = () => {
  // close返回的就是关闭dialog的方法,所以button里直接调用就可
    const close = modal(<h1>小改改<Button onClick={() => close()}>关闭</Button></h1>)
  }

<Button onClick={() => openModal() }>modal</Button>

代码:https://github.com/wanglifa/IReact-UI/tree/94c35f30f67f48d115bd3c7770f90dbf18b61083

相关文章