likes
comments
collection
share

这样写弹框逻辑,有更多时间摸鱼了!

作者站长头像
站长
· 阅读数 23

大家好,我是多喝热水。

不知道大家平时在业务中都是怎么去写弹框逻辑的,今天和大家聊一聊如何在业务中优雅的使用弹框~

假设现在有一个场景:网站里有个评论区,这个评论区需要登录后才能使用,如果用户没登录的情况下点击了这个按钮,那么我们需要弹出一个登录框让用户先登录,这是一个非常常见的需求吧,如果是你的话你会怎么做呢?

这样写弹框逻辑,有更多时间摸鱼了!

方式一

我们先用最简单的方式来实现,假设我们在业务中使用的是 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
评论
请登录