这样写弹框逻辑,有更多时间摸鱼了!
大家好,我是多喝热水。
不知道大家平时在业务中都是怎么去写弹框逻辑的,今天和大家聊一聊如何在业务中优雅的使用弹框~
假设现在有一个场景:网站里有个评论区,这个评论区需要登录后才能使用,如果用户没登录的情况下点击了这个按钮,那么我们需要弹出一个登录框让用户先登录,这是一个非常常见的需求吧,如果是你的话你会怎么做呢?
方式一
我们先用最简单的方式来实现,假设我们在业务中使用的是 Ant Design,那么我们写出来的代码应该是这样的:
export default function LoginTrigger(props: LoginTriggerProps) {
const [isModalOpen, setIsModalOpen] = useState(false);
const showModal = () => {
setIsModalOpen(true);
};
const handleOk = () => {
setIsModalOpen(false);
};
const handleCancel = () => {
setIsModalOpen(false);
};
return (
<>
<Button type="primary" onClick={showModal}>
登录
</Button>
<Modal
title="Basic Modal"
open={isModalOpen}
onOk={handleOk}
onCancel={handleCancel}
>
登录框内容,此处省略输入框....
</Modal>
此处省略一大坨业务代码...
</>
);
}
ok,我们来看一下效果如何:
和我们预想的一样,点击登录按钮,它确实给我们弹出了登录框。但是我们得思考一下,这样做有什么弊端?
1)如果我们不止评论区有这个登录的需求,像发文章、私信、甚至是有些页面加载完毕后就需要弹登录,那像这样每一个场景我们都需要 再写一遍 这样的代码吗?
2)不利于代码的维护,因为我们业务中可能不仅仅有登录弹框,也可能有其他弹框逻辑,这么多 Modal
混合在一起,维护起来需要耗费大量的精力,效率极低。
所以,为了我们以后能有更多时间去摸鱼,我们必须得把这些问题都消灭!
现在问题已经抛出来了,怎么解决?有点经验的小伙伴可能会说,这些弹框大概率都是重复代码,我们把它封装成一个组件不就好了吗?
方式二
直接开干,我们把这个登录弹框的逻辑给封装一下,代码如下:
export default function useLoginModal() {
const [isModalOpen, setIsModalOpen] = useState<boolean>(false);
const showLoginModal = () => {
setIsModalOpen(true);
};
const closeLoginModal = () => {
setIsModalOpen(false);
};
const LoginModal = useCallback(() => {
if (!isModalOpen) return <></>;
return ReactDOM.createPortal(
<Modal
open={isModalOpen}
onCancel={closeLoginModal}
>
登录框内容,此处省略输入框....
</Modal>,
document.body
);
}, [isModalOpen]);
return { showLoginModal, LoginModal };
}
然后我们后续使用这个登录弹框只需要调用 useLoginModal
函数即可,如下:
export default function LoginTrigger(props: LoginTriggerProps) {
const { LoginModal, showLoginModal } = useLoginModal();
return (
<>
<Button type="primary" onClick={showLoginModal}>
登录
</Button>
<LoginModal />
此处省略一大坨业务代码...
</>
);
}
ok,我们再看看效果:
nice,依然是一样的效果,但是,怎么感觉还是怪怪的。
整个流程需要调用 useLoginModal
函数,又要使用一下 LoginModal
组件,最后再调一下 showLoginModal
才能弹框。。。。
你干嘛~哎呦!我就只想弹个框而已,你给我搞这么一堆代码。。。。
最佳实践
上面的代码只是借助封装的特性,让我们少写了一些代码,但总的流程并没有改变,所以我们理想的效果,就是调用一个函数就可以弹框,能不能做到?答案是可以的,下面我们就来实践一下,我认为的最佳方案。
1)这里我们引入一个包 @ebay/nice-modal-react
,使用方式如下:
import NiceModal from "@ebay/nice-modal-react";
import { ReactNode } from "react";
export default function GlobalProvider({ children }) {
return <NiceModal.Provider>{children}</NiceModal.Provider>;
}
这一步我们将整个根组件包裹在 NiceModal.Provider
中
2)编写弹框组件内容
import NiceModal, { useModal } from "@ebay/nice-modal-react";
import { Modal } from "antd";
export default NiceModal.create((props) => {
const modalInstance = useModal();
return (
<Modal
title="用户登录"
open={modalInstance.visible}
onOk={modalInstance.hide}
onCancel={modalInstance.hide}
>
登录弹框最佳实践,此处省略一些输入框
</Modal>
);
});
这一步,我们使用 NiceModal 创建了一个登录弹框
3)使用弹框
import loginModal from "@/components/globalModal/login";
import NiceModal from "@ebay/nice-modal-react";
import { Button } from "antd";
export default function LoginTrigger(props) {
return <Button onClick={() => NiceModal.show(loginModal)}>登录</Button>;
}
loginModal
就是我们上面创建的弹框,接下来我们看看效果如何:
依旧没有任何问题,我们现在通过调用一次函数就实现了弹框,但我们依然引入了两个新组件,如果还是觉得不够优雅,我们还可以再优化一下,如下:
import NiceModal from "@ebay/nice-modal-react";
import LoginModal from "@/components/globalModal/login";
export const showLogin = () => NiceModal.show(LoginModal);
这里我们将这段弹框逻辑封装成一个工具函数,后续我们直接调用 showLogin
即可,维护的时候也只需要维护这个工具函数。
总结
最后一种方式弹框,把我们抛出的问题都消灭了,当然这里仅仅只是以登录为例,如果有其他弹框逻辑我们只需要创建新的 xxxModal
,再通过 NiceModal.show(xxxModal)
即可。
而且通过发布订阅模式,我们也可以自己实现一个 nice-modal-react
,但全部写下来篇幅有点长了,感兴趣的话我们可以后面来搞一搞。
好了,这篇文章就肝到这里,如果有更好的方式大家可以探讨一下。
转载自:https://juejin.cn/post/7396361501790388234