关于 dialog(对话框),回想一下,我的代码经历过好几次变化,使用 React 之前,React 初级阶段,目前部分项目使用原生 HTML 部分项目使用 Remix framework。
tl;dr
React 之前使用过 jQuery, Ext JS, pure JS 编写 UI,一般使用命令式编写 dialog UI。
// jQuery code
$("#boring").click(function() {
$.dialog({
"body": "jQueryScript.net!",
"title": "jQuery Dialog Plugin Demo",
"show": true
});
});
// pure JS
window.alert("hello world")
// sweetalert2
Swal.fire({
title: "Good job!",
text: "You clicked the button!",
icon: "success"
});
刚开始使用 React 时,几乎都有代码都是声明式的,一般如下:
let [isOpen, setIsOpen] = useState(true)
function closeModal() {
setIsOpen(false)
}
function openModal() {
setIsOpen(true)
}
return (
<>
<button onClick={openModal}>open</button>
<Dialog open={isOpen} onClose={closeModal} >
{* ... *}
</Dialog>
</>
)
不过这种写法有很多问题:
<AlertDialog>
<AlertDialogTrigger asChild>
<Button variant="outline">Show Dialog</Button>
</AlertDialogTrigger>
<AlertDialogContent>
{* ... *}
</AlertDialogContent>
</AlertDialog>
使用一个 AlertDialogTrigger 可以避免 useState 爆炸,算是一点点改善🤏
<dialog id="my_modal_2" className="modal">
<div className="modal-box">
<h3 className="font-bold text-lg">Hello!</h3>
<p className="py-4">Press ESC key or click outside to close</p>
</div>
<form method="dialog" className="modal-backdrop">
<button>close</button>
</form>
</dialog>
// close dialog
document.getElementById('my_modal_2').showModal()
这种方式不仅避免了 useState 爆炸,又可以在任意时机操作 dialog。很棒。
缺点就是在 React 项目中需要一个 useRef 配合。
const dialogRef = useRef<HTMLDialogElement>(null);
function open() {
dialogRef.current?.showModal();
}
<dialog ref={dialogRef} className="modal">
{* ... *}
</dialog>
在需要使用 Modal 的页面中埋下一个 <Outlet />
,然后实现一个 Modal Route 如下:
// members.$id.tsx
<Link to={"disable"}>disable</Link>
// members.$id.disable.tsx
export async function action() {
// your code
return redirect("/members/${id}")
}
export default function MemberDisableRoute() {
const navigate = useNavigate()
return (
<dialog id={props.title} className="modal modal-open">
<div className="modal-box">
<h3 className="font-bold text-lg">Are you sure?</h3>
<div className="modal-action">
<button className="btn" onClick={() => navigate(-1)}>Cancel</button>
<Form method="POST">
<input name="id" value="id" hidden />
<button className="btn">Yes</button>
</Form>
</div>
</div>
</dialog>
);
}
不仅没有 useState 数量爆炸,也不需要手工维护 dialog 状态。
其他优点:
缺点:
背景资料: