Before starting please notice that the source code is availbe here: https://codesandbox.io/s/getconfirm-n48ec?file=/src/getConfirm.tsx
Goal
This article aims to create a simple and effective way for getting user confirmation and showing messages with a readable syntax like this:
const res = await getConfirm();
if (res) {
console.log("user confirmed");
}
For getting user confirmation first we need a modal. This modal can be our custom modal or a component from any components library.
Then we need a way to control showing and hiding modal.
Previously I was using react state for conditional rendering of modal like bellow:
import React, { useState } from "react";
import Modal from "./components/Modal/Modal";
function App() {
const [isOpen, setIsOpen] = useState(false);
const onConfirm = () => {
console.log("user has confirmed");
};
const startTheProcess = () => {
setIsOpen(true);
};
return (
<div>
{isOpen && (
<Modal
text={"are you sure you want to continue?"}
onCancel={() => {
setIsOpen(false);
}}
onConfirm={onConfirm}
/>
)}
<button onClick={startTheProcess}> start </button>
</div>
);
}
export default App;
But if we have more than one confirm box in our component, we need to add more states in our page and that can create a mess with increasing our need to more boxes.
This approach wasn't ideal for me because:
- Tracking this code was hard
- I needed to be sure that only one modal would be shown up at the same time.
- Sometimes I needed to get user confirmation or show a message box outside of React's component, which I mean in regular JavaScript.
I searched about this and I find out there is another way for showing our modals using react-dom.
And that is what I want to share with you today.
So first let's create our web application:
npx create-react-app --template typescript get-confirm
Then we create our modal component:
// in src/components/Modal/Modal.tsx
import React from "react";
import "./Modal.css";
type Props = {
onConfirm: () => void;
onCancel: () => void;
text: string;
};
const Modal: React.FC<Props> = (props) => {
const onConfirm = () => {
props.onConfirm();
};
const onClose = () => {
props.onCancel();
};
return (
<div>
<div className="content">
<div className={"text"}>{props.text}</div>
<div className={"footer"}>
<button onClick={onConfirm}>yes</button>
<button onClick={onClose}>no</button>
</div>
</div>
<div onClick={onClose} className="overlay"></div>
</div>
);
};
export default Modal;
And let's add some css for our modal:
/* in src/components/Modal/Modal.css */
.content {
position: fixed;
top: 50%;
left: 50%;
background-color: #fff;
z-index: 1;
border-radius: 8px;
padding: 16px;
transform: translate(-50%, -50%);
}
.text {
margin-bottom: 16px;
}
.footer {
display: flex;
justify-content: space-between;
}
.overlay {
position: fixed;
inset: 0;
background: rgba(0, 0, 0, 0.7);
}
Please notice that this is just a simple modal that I've created for this tutorial. You probably want to use your own.
we need to add a new container to our index.html file in public folder. we add a new div element with id of modal so we can render our modal using it.
So our index.html file would be like this:
<!-- in public/index.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<meta name="description" content="Web site created using create-react-app" />
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
<!--
manifest.json provides metadata used when your web app is installed on a
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
-->
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<!--
Notice the use of %PUBLIC_URL% in the tags above.
It will be replaced with the URL of the `public` folder during the build.
Only files inside the `public` folder can be referenced from the HTML.
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
work correctly both with client-side routing and a non-root public URL.
Learn how to configure a non-root public URL by running `npm run build`.
-->
<title>React App</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
<div id="modal"></div>
<!--
This HTML file is a template.
If you open it directly in the browser, you will see an empty page.
You can add webfonts, meta tags, or analytics to this file.
The build step will place the bundled scripts into the <body> tag.
To begin the development, run `npm start` or `yarn start`.
To create a production bundle, use `npm run build` or `yarn build`.
-->
</body>
</html>
Here is the important part when we create our getConfirm function.
So in the src folder we create a file named getConfirm.tsx:
// in src/getConfirm.tsx
import * as ReactDOM from "react-dom";
import Modal from "./components/Modal/Modal";
const modalRoot = document.getElementById("modal");
const getConfirm = (text = "are you sure?") => {
return new Promise((resolve) => {
const onConfirm = () => {
modalRoot && ReactDOM.unmountComponentAtNode(modalRoot);
resolve(true);
};
const onCancel = () => {
modalRoot && ReactDOM.unmountComponentAtNode(modalRoot);
resolve(false);
};
ReactDOM.render(
<Modal onConfirm={onConfirm} onCancel={onCancel} text={text}>
{text}
</Modal>,
modalRoot
);
});
};
export default getConfirm;
What we are doing here:
- rendering our Modal component using ReactDOM.render.
- onConfirm: remove modal and resolve true.
- onCanel: remove modal and resolve false.
We are finished now and we can use our function.
For example in App.tsx:
import getConfirm from "./getConfirm";
function App() {
const startTheProcess = async () => {
const res = await getConfirm();
if (res) {
console.log("user confirmed");
}
};
return (
<div>
<button onClick={startTheProcess}> do something</button>
</div>
);
}
export default App;
and that's it.
You can create showMessage function simply according to what we've learned in this tutorial too.
Please let me know if you have any questions.
Thanks for reading
Top comments (3)
Really enjoyed reading through your article and learned a lot thanks for sharing your knowledge 👏
Wow ! Amazing article... Keep writing please 💥
Awesome content, keep sharing such this nice articles ⚡