前言
在 React 项目中,弹窗管理一直是一个常见的需求。传统的弹窗管理方式往往需要在组件中维护多个 state,代码繁琐且不易维护。今天分享一个基于 Hook 的弹窗管理方案,让弹窗管理变得更加优雅。
实现目标
- 简化弹窗的打开和关闭操作
- 支持动态传递参数
- 自动销毁不需要的弹窗实例
核心实现
tsx
import { useMemoizedFn } from "ahooks";
import React, { cloneElement, isValidElement, useRef, useState } from "react";
export type DialogProps = { onOpenChange: (open: boolean) => void; open: boolean,afterClose: ()=> void };
/**
* 弹窗
* @description 通过hook管理弹窗
* @example
* const [dialog, dialogContextHolder] = useDialog(Com)
*
* dialog.show({})
* dialog.close()
*
* return (<>{dialogContextHolder}</>)
*/
export function useDialog<T = object>(dialogRender: any) {
const [open, setOpen] = useState(false);
const [destroyDialogRender,setDestroyDialogRender] = useState(true)
const propsRef = useRef<T | null>(null);
const onOpenChange = useMemoizedFn((open: boolean)=>{
setOpen(open)
})
const afterClose = useMemoizedFn(() => {
setDestroyDialogRender(true)
})
const show = useMemoizedFn((props?: T) => {
propsRef.current = props ?? null;
setDestroyDialogRender(false)
onOpenChange(true);
});
const close = useMemoizedFn(() => {
propsRef.current = null;
onOpenChange(false);
});
const memoizedDialogRender = useMemoizedFn(() => {
const props = {
...propsRef.current,
open,
onOpenChange,
afterClose
} as DialogProps & T;
try {
if (isValidElement(dialogRender)) {
return cloneElement(dialogRender, props);
}
if (typeof dialogRender === "function") {
return React.createElement(dialogRender, props);
}
console.error("dialogRender must be a ReactElement or a function");
return null;
} catch (error) {
console.error("Dialog render failed:", error);
return null;
}
});
const dialogContextHolder = destroyDialogRender ? null : memoizedDialogRender();
return [
{
show,
close,
open,
},
dialogContextHolder,
];
}
核心方法解析
- 显示和关闭方法
tsx
const show = useMemoizedFn((props: T) => {
propsRef.current = props;
setDestroyDialogRender(false);
onOpenChange(true);
});
const close = useMemoizedFn(() => {
propsRef.current = null;
onOpenChange(false);
});
- 弹窗渲染逻辑
tsx
const memoizedDialogRender = useMemoizedFn(() => {
const props = {
...propsRef.current,
open,
onOpenChange,
afterClose
} as DialogProps & T;
if (isValidElement(dialogRender)) {
return cloneElement(dialogRender, props);
}
if (typeof dialogRender === "function") {
return React.createElement(dialogRender, props);
}
return null;
});
使用示例
tsx
import { useDialog,afterClose, type DialogProps } from './useDialog';
import React,{ FC } from 'react';
import { Button, Modal } from 'antd';
// 弹窗组件
const MyDialog:FC<{name: string} & DialogProps> = ({ open, onOpenChange,afterClose, name }) => (
<Modal open={open} onCancel={() => onOpenChange(false)} afterClose={afterClose}>
<div>Hello, {name}!</div>
</Modal>
);
// 使用 Hook
const MyComponent = () => {
const [dialog, dialogHolder] = useDialog<{name: string}>(MyDialog);
const handleClick = () => {
dialog.show({ name: 'React' });
};
return (
<>
<Button onClick={handleClick}>打开弹窗</Button>
{dialogHolder}
</>
);
};
通过这个 Hook,我们可以用更优雅的方式管理弹窗状态。如果觉得文章有帮助,欢迎点赞转发!