简体   繁体   English

如何使用警报/确认/提示模式实现使用模式挂钩?

[英]How to implement a use-modal-hook with a alert/confirm/prompt pattern?

I've started using ReactModal to display warning, prompts and such to the user.我已经开始使用ReactModal向用户显示警告、提示等。 This works well, but you have to add the component and isOpen state and callbacks everywhere you want to maybe display a modal window.这很好用,但是您必须添加组件和isOpen state 和回调,无论您想在哪里显示模态 window。 Maybe I've misunderstood how to use it properly, but this pattern quickly becomes tedious.也许我误解了如何正确使用它,但是这种模式很快就会变得乏味。

I want to implement a sort of alert, confirm, prompt pattern where I can just say eg:我想实现一种警报、确认、提示模式,我可以说例如:

const { myAlert, myConfirm } = useModal();

if(myConfirm('Show an alert?')) {
  myAlert('Alert!');
}

to show my modal in the desired way.以所需的方式显示我的模态。

My problem lies with the functions.我的问题在于功能。 I want the app to wait for the user to click something in the modal before the following code is executed.我希望应用程序在执行以下代码之前等待用户单击模式中的某些内容。 Now my functions just instantly return after executing.现在我的函数在执行后立即返回。

Here is my hook:这是我的钩子:

import { useContext } from 'react';
import { ModalContext } from '../App';
import Alert from '../components/Modals/Alert';
import Confirm from '../components/Modals/Confirm';

const useModal2 = () => {
  const { openModal, closeModal } = useContext(ModalContext);

  const alert = (message: string) => {
    openModal(
      <Alert title="Warning" message={message} closeAlert={closeModal} />
    );
  };
  const confirm = (question: string): boolean | undefined => {
    let answer;
    const closeConfirm = (ok: boolean) => {
      answer = ok;
      closeModal();
    };
    openModal(
      <Confirm
        title="Warning"
        question={question}
        closeConfirm={closeConfirm}
      />
    );
    return answer;
  };

  return { alert, confirm };
};

export default useModal2;

here is my Context and its values:这是我的上下文及其值:

interface ModalContextType {
  openModal: (modal: ReactNode) => void;
  closeModal: () => void;
}
export const ModalContext = createContext<ModalContextType>({
  openModal: () => {},
  closeModal: () => {},
});
...
const App = () => {
...
  const [modalContent, setModalContent] = useState<ReactNode>(null);
  const [isModalOpen, { openModal, closeModal }] = useModal();
  const open = (modal: ReactNode) => {
    setModalContent(modal);
    openModal();
  };
  const close = () => {
    setModalContent(null);
    closeModal();
  };
...
return (
  <ModalContext.Provider value={{ openModal: open, closeModal: close }}>
...
        <ReactModal
          className={styles.modalContent}
          overlayClassName={styles.modalOverlay}
          isOpen={isModalOpen}
          onRequestClose={close}
        >
          {modalContent}
        </ReactModal>
    ...

If I click the following button, the onClick function just outputs undefined before I click anything in the confirm dialog:如果我单击以下按钮,则 onClick function 将在我单击确认对话框中的任何内容之前输出 undefined :

      <button
        onClick={() => {
          const answer = confirm('Show an alert?');
          if (answer) {
            alert('The alert you requested!');
          }
          console.log(answer);
        }}
      >
        Confirm!
      </button>

how can I make it wait for my answer in the dialog before it checks the value in the if and runs the console.log?在它检查if中的值并运行 console.log 之前,如何让它在对话框中等待我的回答? Any ideas?有任何想法吗?

Here is my code in codesandbox: https://codesandbox.io/s/blue-architecture-jtjyr这是我在代码框中的代码: https://codesandbox.io/s/blue-architecture-jtjyr

To answer your main question:要回答您的主要问题:

Your confirm function will return as soon as it is called.您的confirm function 将在调用后立即return It will not wait for closeConfirm to set the answer variable, because JS functions are synchronous by nature, their content is executed from top to bottom right away.它不会等待closeConfirm设置answer变量,因为 JS 函数本质上是同步的,它们的内容是从上到下立即执行的。 Except for asyncronous functions or generators, which will probably not solve your problem.除了异步函数或生成器,它们可能无法解决您的问题。 You could probably solve it with a state variable but that will complicate things even further.您可能可以使用 state 变量来解决它,但这会使事情变得更加复杂。

const confirm = (question) => {
    let answer;
    const closeConfirm = (ok) => {
      answer = ok;
      closeModal();
    };
    openModal(
      <Confirm
        title="Warning"
        question={question}
        closeConfirm={closeConfirm}
      />
    );
    return answer;
  };

You could try to visualize the execution of that snippet to better understand it.您可以尝试可视化该片段的执行以更好地理解它。 JS execution visualizer JS 执行可视化工具

Furthermore it seems like you are overcomplicating things by a lot here.此外,您似乎在这里使事情复杂化了很多。 I think you don't need context here, but I don't know the whole context of your app, pun intended;).我认为您在这里不需要上下文,但我不知道您的应用程序的整个上下文,双关语;)。 You are overriding browser globals like alert() , which is a bad practive imo.您正在覆盖诸如alert()之类的浏览器全局变量,这是一种糟糕的 imo。 You are mapping and nesting functions like open > openModal > alert... And your naming is not descriptive.您正在映射和嵌套函数,例如 open > openModal > alert... 而且您的命名不是描述性的。 Like what is the difference between useModal and useModal2?比如 useModal 和 useModal2 有什么区别? So in total the code is hard to read.所以总的来说,代码很难阅读。

Here is a codesandbox to give you an idea of how you could solve it in a slightly more readable way.这是一个 代码框,可让您了解如何以更易读的方式解决它。

Just had to return a Promise from the functions supplied by my hook.只需要从我的钩子提供的函数中返回一个 Promise 。

import { useContext } from 'react';
import { ModalContext } from '../App';
import Alert from '../components/Modals/Alert';
import Confirm from '../components/Modals/Confirm';

const useDialog = () => {
  const { openModal, closeModal } = useContext(ModalContext);

  const alertDialog = (message: string) => {
    return new Promise((resolve) => {
      const closeAlert = () => {
        resolve(true);
        closeModal();
      };

      openModal(
        <Alert title="Warning" message={message} closeAlert={closeAlert} />
      );
    });
  };
  const confirmDialog = (message: string) => {
    return new Promise<boolean>((resolve) => {
      const closeConfirm = (value: boolean) => {
        resolve(value);
        closeModal();
      };

      openModal(
        <Confirm
          title="Warning"
          message={message}
          closeConfirm={closeConfirm}
        />
      );
    });
  };

  return { alertDialog, confirmDialog };
};

export default useModal2;

then you can use it like so:那么你可以像这样使用它:

      <button
        onClick={() => {
          const answer = await confirmDialog('Show an alert?');
          if (answer) {
            await alertDialog('The alert you requested!');
          }
          console.log('Done showing dialogs for now!');
        }}
      >
        Confirm!
      </button>

Would probably be cleaner to take care of the async/await somewhere else than where you open the dialogs.与打开对话框的地方相比,在其他地方处理异步/等待可能会更干净。 But thats a problem for another time.但这是另一个问题。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM