DEV Community

glocore for Metamodal

Posted on

Control a Dialog Box Asynchronously using React Hooks

We often control dialog boxes by including the component in our JSX and controlling its visibility and behaviour with state variables and props.

Most of the time however, these are pretty standard components that require very little data to render, like a confirmation dialog box, for example. With hooks, we should be able to do something like this:

const { getConfirmation } = useConfirmationDialog();

// ...

const confirmed = await getConfirmation({
  title: "Attention!",
  message: "Are you sure you would like to delete this entry?",
});

if (confirmed) {
  // perform operation
}
Enter fullscreen mode Exit fullscreen mode

In this post, we will create the above hook. This will allow us to have a single confirmation dialog box at an app-level, and communicate with it using the context API.

An example implementation of this can be found in this CodeSandbox.

Step 1: Setup the context

Create an empty context object and a provider HOC, which we can later wrap at an app level.

import React from "react";
import ConfirmationDialog from "./ConfirmationDialog";

const ConfirmationDialogContext = React.createContext({});

const ConfirmationDialogProvider = ({ children }) => {
  return (
    <ConfirmationDialogContext.Provider>
      <ConfirmationDialog />
      {children}
    </ConfirmationDialogContext.Provider>
  );
};
Enter fullscreen mode Exit fullscreen mode

Step 2: Add the control mechanism

Here we add a state variable dialogOpen to control the visibility of the dialog. We also create another state variable called dialogConfig, which would contain the dialog content and action callback.

We then use these to create trigger functions for displaying the dialog, as well as handler functions that are called when the action buttons are pressed.

const ConfirmationDialogProvider = ({ children }) => {
  const { dialogOpen, setDialogOpen } = React.useState(false);
  const { dialogConfig, setDialogConfig } = React.useState({});

  const openDialog = ({ title, message, actionCallback }) => {
    setDialogOpen(true);
    setDialogConfig({ title, message, actionCallback });
  };

  const resetDialog = () => {
    setDialogOpen(false);
    setDialogConfig({});
  };

  const onConfirm = () => {
    resetDialog();
    dialogConfig.actionCallback(true);
  };

  const onDismiss = () => {
    resetDialog();
    dialogConfig.actionCallback(false);
  };

  return (
    <ConfirmationDialogContext.Provider value={{ openDialog }}>
      <ConfirmationDialog
        open={dialogOpen}
        title={dialogConfig.title}
        message={dialogConfig.message}
        onConfirm={onConfirm}
        onDismiss={onDismiss}
      />
      {children}
    </ConfirmationDialogContext.Provider>
  );
};
Enter fullscreen mode Exit fullscreen mode

Step 3: Create a hook to trigger the dialog

Use this hook to trigger the dialog from anywhere in your app. It returns a function that lets you await the user input. This would let you use the async/await syntax rather than passing in a callback.

const useConfirmationDialog = () => {
  const { openDialog } = React.useContext(ConfirmationDialogContext);

  const getConfirmation = ({ ...options }) =>
    new Promise((res) => {
      openDialog({ actionCallback: res, ...options });
    });

  return { getConfirmation };
};
Enter fullscreen mode Exit fullscreen mode

Step 4: Add the provider HOC at the app level

Finally, we wrap that HOC we created in Step 1 around our app (or at whatever level you prefer).

const App = () => {
  return (
    <ConfirmationDialogProvider>
      {/* Your app here */}
    </ConfirmationDialogProvider>
  );
}
Enter fullscreen mode Exit fullscreen mode

Usage:

const { getConfirmation } = useConfirmationDialog();

// ...

const confirmed = await getConfirmation({
  title: "Attention!",
  message: "Are you sure you would like to delete this entry?",
});

if (confirmed) {
  // perform operation
}
Enter fullscreen mode Exit fullscreen mode

Once again, here's a CodeSandbox of an example implementation.

Typos? Improvements?

Thanks for reading. If you notice any typos or would like to suggest improvements, please feel free to either create an issue or PR here.

Top comments (0)