- 这里记录一下查看antd 组件库时学到的东西,或者是想法
Modal
其实我们可以简单想一下我们需要一个Modal 框那些属性是必要的,很容易想象的到就是visible
, onCancel
, onOk
这几个属性, 因为这几个属性是
控制我们这个Modal
显示隐藏的
所以就有了以下下简易传值
const { toggle, setToggle } = useToggle();
<Modal
visible={toggle}
onCancel={() => setToggle(false)}
onOk={() => doSome()}
>
</Modal>
Modal.tsx
查看源码Modal.tsx
, 这个组件很简单,其实就是使用了rc-component
的rc-dialog
, 把我们传的visble
, onCancel
等传到 Dialog
里面
import Dialog from 'rc-dialog'
interface Iprop {
visible: boolean,
onCancel: () => void,
onOk: () => void
}
const Modal: React.FC<Iprops> = (props: Iprops) => {
const { visbile, onCancel, onOk } = props;
const handleCancel = () => {
onCancel?.()
}
const handleOk = () => {
onOk?.()
}
return (
<Dialog
visible={visible}
onCancel={handleCancel}
onOk={handleOk}
>
)
}
在Modal
中我们还有一些用法,比如:
Modal.confirm({
title: 'hello,',
content: 'world',
onOk() {
console.log('OK');
},
onCancel() {
console.log('Cancel');
},
});
这样子可以直接调出Modal
看看Modal.confirm
的实现源码地址
Modal.confirm
它用了一个高阶函数confirm(withxxx(props))
, withxxx
函数就是给props 附加一个type
, 即 类似Object.assign(props, { type: 'xxx' })
;
Modal.info = function infoFn(props: ModalFuncProps) {
return confirm(withInfo(props));
};
- withInfo 函数
/** withInfo 函数 */ function withInfo(props: ModalFuncProps): ModalFuncProps { return { icon: <InfoCircleOutlined />, okCancel: false, ...props, type: 'info', }; }
重头戏 Confirm 函数
其实就是在body
下面 创建一个节点const div = document.createElement('div');
然后将div
append 到body下, 最后调用 React.render(Modal, div)
将model 渲染到div上,
function confirm(config: ModalFuncProps) {
const div = document.createElement('div');
document.body.appendChild(div);
let currentConfig = { ...config, close, visible: true } as any;
/** destroy 的时候将visbile 改成false,然后如果有onCancel 就执行onCancle, 将当前div remove掉 **/
function destroy(...args: any[]) {
const unmountResult = ReactDOM.unmountComponentAtNode(div);
if (unmountResult && div.parentNode) {
div.parentNode.removeChild(div);
}
const triggerCancel = args.some(param => param && param.triggerCancel);
if (config.onCancel && triggerCancel) {
config.onCancel(...args);
}
for (let i = 0; i < destroyFns.length; i++) {
const fn = destroyFns[i];
if (fn === close) {
destroyFns.splice(i, 1);
break;
}
}
}
/** render 到div上 */
function render(props: { visible: boolean, onOk: () => void, onCancel: () => void }) {
setTimeout(() => {
ReactDOM.render(
<ConfirmDialog
{...props}
/>,
div,
);
});
}
/** close 的时候将visbile 改成false,然后如果有afterClose afterClose, 然后destroy掉 **/
function close(...args: any[]) {
currentConfig = {
...currentConfig,
visible: false,
afterClose: () => {
if (typeof config.afterClose === 'function') {
config.afterClose();
}
destroy.apply(this, args);
},
};
render(currentConfig);
}
/** 向外暴露update 方法,可以修改props等,修改完props然后render一下 */
function update(configUpdate: ConfigUpdate) {
if (typeof configUpdate === 'function') {
currentConfig = configUpdate(currentConfig);
} else {
currentConfig = {
...currentConfig,
...configUpdate,
};
}
render(currentConfig);
}
render(currentConfig);
destroyFns.push(close);
/** 暴露update 和destroy 方法 */
/** 可以用 const modal = Modal.info({title: '123'}) */
/** 可以用 modal.update({title: '345'}) 去更新title*/
/** 可以用 modal.destory() 去关闭modal*/
return {
destroy: close,
update,
};
}
注意看这个<ConfirmDialog />
ConfirmDialog
这个ConfirmDialog 里面的确认和取消 按钮都用了一个<ActionButton onOk={onOk}/>
其实就是一个<Button />
下面只看他如何处理onOk
方法
const ActionButton = () => {
const handlePromiseOnOk = (returnValueOfOnOk?: PromiseLike<any>) => {
const { close } = props;
if (!isThenable(returnValueOfOnOk)) {
return;
}
setLoading(true);
/** promise 完成后直接close 掉 */
returnValueOfOnOk!.then(
(...args: any[]) => {
setLoading(false);
close(...args);
clickedRef.current = false;
},
(e: Error) => {
console.error(e);
setLoading(false);
clickedRef.current = false;
},
);
};
const onClick = (e: React.MouseEvent<HTMLButtonElement>) => {
const { actionFn, close } = props;
// 这里处理按钮是否被点击了,如果是, 直接return掉
if (clickedRef.current) {
return;
}
clickedRef.current = true;
/** 如果没有onOk 或者是onCancel 方法,直接调用modal.close() 方法,光比弹窗 */
if (!actionFn) {
close();
return;
}
let returnValueOfOnOk;
/** 这里我的理解是 处理是否冒泡? */
if (props.emitEvent) {
returnValueOfOnOk = actionFn(e);
/** 判断是否是一个promise , 处理onOk 直接return new Promise() */
if (props.quitOnNullishReturnValue && !isThenable(returnValueOfOnOk)) {
clickedRef.current = false;
close(e);
return;
}
} else if (actionFn.length) {
/** onOk 是否有参数 function onOk(close) 的情况, */
returnValueOfOnOk = actionFn(close);
clickedRef.current = false;
} else {
/** 无参数 */
returnValueOfOnOk = actionFn();
if (!returnValueOfOnOk) {
close();
return;
}
}
/** 处理promise 方法 */
handlePromiseOnOk(returnValueOfOnOk);
};
return (
<Button OnClick={onClick}>
</Button>
)
}