弹窗在项目业务中很常见,接下来我们将基于 antd 的 Modal 组件实现 2 次封装。这个例子我们将实现一个选择城市的 SelectCityDialog 组件。
基本思路就是:
(1)弹窗组件的显示和隐藏由 SelectCityDialog 内部一个变量 visable 来控制,而这个值为 true 或者 false 则是由我们传入的值来控制。也就是说父组件传值给 SelectCityDialog 控制显示和隐藏
(2)由于我们要在外部(父组件)来打开这个弹窗,所以我们要在外部定义一个变量比如我们就叫 vis,它每次值的变化则会传递给 SelectCityDialog
(3)在 SelectCityDialog 内部,我们要点击取消或确定会关闭弹窗,而我们说过弹窗组件的显示和隐藏由 SelectCityDialog 内部一个变量 visable 来控制,而这个值为 true 或者 false 则是由我们传入的值来控制,而不是 SelectCityDialog 本身的 visable 来更改,所以关闭的时候我们需要让外部 vis 的值更改,进而引发 SelectCityDialog 传递过来的值发生更改。那怎么办呢?只能给组件传递一个函数,在关闭的时候让调用父组件的函数更改父组件内的 vis 的值更改,这样就能达到目的。
有点绕。但是只要我们理清楚这样一个关系就可以了。
(1)SelectCityDialog 内部的 visible===>控制 SelectCityDialog 显示、隐藏
(2)父组件的 vis=====>传值给 SelectCityDialog=====>引发 visible 变更
(3)SelectCityDialog 关闭=======>调用父组件的函数====>设置父组件的 vis 为 false,再次引发子组件的 visible 变更
接下来我们实现代码:
父组件,通过点击按钮打开弹窗 import SelectCityDialog from "../../components/SelectCityDialog/index" import style from "./index.module.less" function HouseStyle(props){ const [visiable,setVisiable]=useState(false) const operDialogFunc=(flag)=>{ setVisiable(flag) } return( <div> <Button size="large" onClick={()=>operDialogFunc(true)} icon={<IconFont type="icon-dingwei" style={{fontSize:"18px",color:"#ff8c00"}} />}> 打开弹窗 </Button> <SelectCityDialog title="设置常用城市" vis={visiable} operDialogFunc={operDialogFunc} > </SelectCityDialog> </div> ) } export default HouseStyle
接下来是 selectCityDialog 组件:
export default function SelectCityDialog(props){ let {title,operDialogFunc,vis}=props; const [cityVisable,setCityVisable]=useState(false) useEffect(()=>{ console.log("vis:",vis) setCityVisable(vis) },[vis]) return( <Modal title={title} centered visible={cityVisable} onOk={() =>operDialogFunc(false)} onCancel={() => operDialogFunc(false)} width={1000} > <div className={style['cur-locate']}> <div className={style['list-lf']}>当前定位</div> <div className={style['list-rf']}></div> </div> <div className={style['recently-select']}> <div className={style['list-lf']}>最近选择</div> <div className={style['list-rf']}></div> </div> <div></div> </Modal> ) }
注意我们在 useEffect 里对传过来的变量 vis 进行了监听。这样每次 vis 改变,我们都重新对 cityVisable 进行重新赋值。
到此基本上实现了弹窗效果。
优化:
如果直接把该组件引入。这样父组件渲染完成的时候子组件已经先完成挂载。这样会导致一个问题:如果子组件中的数据有问题。这样父组件会直接报错挂掉。我们需要一种机制;在父组件渲染完成以后,子组件是完全不存在的。也就是子组件内的任何报错不会影响到父组件。所以我们需要让子组件最开始是完全销毁的。只有在点击打开的时候才让它真正渲染;实现方式也很简单:
<SelectCityDialog title="设置常用城市" vis={visiable} operDialogFunc={operDialogFunc} > </SelectCityDialog>:null
效果图如下: