What are the use cases that come to our mind when we hear of promises?
- Handling async operations.
- Handling some task that will finish in future.
- Doing something like network or DB calls.
But that's not all to it, we can use it to handle user interactions as well. Let us see how.
Motivation
So, I was building simple confirmation modal for my React project. It needed to be as simple as possible. A modal with some confirmation text in it and two buttons for accepting or declining. Something like this
The simple/straight-forward approach
- Make modal component.
- Mount it conditionally using state.
- Handle delete operation inside Modal component.
The code for it looks something like
import { useState } from "react";
const Modal = ({ setShowModal, handleDelete }) => {
  return (
    <dialog open>
      <p>Are you sure you want to delete?</p>
      <button
        onClick={() => {
          setShowModal(false);
          handleDelete();
        }}
      >
        Yes
      </button>
      <button
        onClick={() => {
          setShowModal(false);
        }}
      >
        No
      </button>
    </dialog>
  );
};
function App() {
  const [showModal, setShowModal] = useState(false);
  const handleDelete = () => {
    //perform delete operation here...
    console.log("hey");
  };
  return (
    <div>
      {showModal && (
        <Modal setShowModal={setShowModal} handleDelete={handleDelete} />
      )}
      <button onClick={() => setShowModal(true)}>Delete</button>
    </div>
  );
}
export default App;
Did you notice the problem?
The above code works all fine, however there are few issues
- We need to maintain a state for showing/hiding modal and pass the setter function to modal.
- We need to pass the business logic to the modal(delete handler).
- The flow of code is not linear anymore, the deletion is being handled by some other component away from the main component(App)
- Not clean code.
The idea of asynchronicity
It was this moment when I felt there must be a better way to handle this. I noticed that a click operation by user is also an async operation! It can happen anytime in future. This is where the idea of Promise kicked in.
The better way
- We can make some custom hook from where a promise is returned which can be awaited upon.
- The promise can resolve to either true or false which can show if the user accepts or rejects.
- The custom hook can also return a Modal component which can be directly use in at the place of application.
- We can also make custom hook configurable to accept different messages.
A Custom hook
import { useState } from "react";
const useConfirmation = () => {
  const [modalState, setModalState] = useState({
    showModal: false,
    resolve: null,
    message: "",
  });
  const Modal = () => {
    return (
      <dialog open={modalState.showModal}>
        <div>
          {modalState.message}
          <button
            onClick={() => {
              setModalState((prev) => ({
                ...prev,
                showModal: false,
              }));
              modalState.resolve(false);
            }}
          >
            Close
          </button>
          <button
            onClick={() => {
              setModalState((prev) => ({
                ...prev,
                showModal: false,
              }));
              modalState.resolve(true);
            }}
          >
            Confirm
          </button>
        </div>
      </dialog>
    );
  };
  const confirm = (message) => {
    return new Promise((resolve) => {
      setModalState((prev) => ({
        resolve: resolve,
        showModal: true,
        message: message,
      }));
    });
  };
  return { Modal, confirm };
};
export { useConfirmation };
The Modal component has it's own state which stores the message, resolve function returned by promise and show state.
Application
import { useConfirmation } from "./useConfirmation.jsx";
function App() {
  const { Modal, confirm } = useConfirmation();
  const handleDelete = async () => {
    const isItOkToDelete = await confirm("Are you sure you want to delete?");
    console.log(isItOkToDelete);
  };
  return (
    <div>
      {Modal()}
      <button onClick={handleDelete}>Delete</button>
    </div>
  );
}
export default App;
The useConfirmation hook returns a function which returns a promise. The function also takes in a message argument to display inside modal. We can await this promise to either receive true/false and proceed with the code flow ahead. 
Thank You
Please do share you feedback and comments. Hope you like it, have a good day.
 
 
              

 
    
Top comments (0)