I’m building a React quiz app where a modal should appear once all answers are correct or when the user loses all the lives. However, the modal opens twice instead of once despite my attempts to control the state. I tried managing the modal display using a state variable showModal. I’ve also attempted adding a flag to control it but the issue persists. What could be causing this issue, and how can I fix it?
const handleModalClose = () => {
setShowModal(false);
};
const Modal = ({ isOpen, onClose }) => {
const modalRef = useRef();
useEffect(() => {
const handleClickOutside = (event) => {
if (modalRef.current && !modalRef.current.contains(event.target)) {
onClose();
}
};
document.addEventListener("mousedown", handleClickOutside);
return () => {
document.removeEventListener("mousedown", handleClickOutside);
};
}, [onClose]);
if (!isOpen) return null;
if (correctAnswersCount >= 9) {
confetti({
particleCount: 100,
spread: 70,
origin: { y: 0.6 },
});
}
return (
<div className="modal-overlay" onClick={onClose}>
<div className="modal-content" ref={modalRef}>
{correctAnswersCount > 9 && (
<h2>Congrats! You've found all the correct answers!</h2>
)}
<h3>
<b>Score</b>
</h3>
<div className="answer-progress-container">
<div className="answer-progress">
<div
className="answer-progress-bar"
style={{ width: `${(correctAnswersCount / 10) * 100}%` }}
>
{correctAnswersCount}/10
</div>
</div>
<p className="streak-p">Current Streak: {streakCounter}</p>
</div>
</div>
<button className="modal-close" onClick={onClose}>
x
</button>
The part where i call the modal, hope it’s clearer now;
{showModal && (
<Modal
isOpen={showModal}
onClose={handleModalClose}
/>
)}
Arda Ciba is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.
3
One option would be to use useEffect
to check the correctAnswersCount
and
lives
:
useEffect(() => {
// assuming you also have state called 'lives'
if (correctAnswersCount >= 9 || lives === 0) {
setShowModal(true);
}
}, [correctAnswersCount, lives]);
// other parts of the code
So,
// Get a hook function
const { useState, useRef, useEffect } = React;
const Modal = ({ isOpen, onClose, correctAnswersCount, lives }) => {
const modalRef = useRef();
useEffect(() => {
const handleClickOutside = (event) => {
if (modalRef.current && !modalRef.current.contains(event.target)) {
onClose();
}
};
document.addEventListener("mousedown", handleClickOutside);
return () => {
document.removeEventListener("mousedown", handleClickOutside);
};
}, [onClose]);
return (
<div className="modal-overlay" onClick={onClose}>
<div className="modal-content" ref={modalRef}>
{correctAnswersCount > 9 && (
<h2>Congrats! You've found all the correct answers!</h2>
)}
<h3>
<b>Score</b>
</h3>
<div className="answer-progress-container">
<div className="answer-progress">
<div
className="answer-progress-bar"
style={{ width: `${(correctAnswersCount / 10) * 100}%` }}
>
{correctAnswersCount}/10
</div>
</div>
</div>
</div>
<button className="modal-close" onClick={onClose}>
x
</button>
</div>);
}
const App = ({ title }) => {
const [correctAnswersCount, setCorrectAnswersCount] = useState(7);
const [lives, setLives] = useState(0);
const [showModal, setShowModal] = useState(false);
const handleModalClose = () => {
// handle closing modal
}
useEffect(() => {
// assuming you also have state called 'lives'
if (correctAnswersCount >= 9 || lives === 0) {
setShowModal(true);
}
}, [correctAnswersCount, lives]);
return (
<div>
<button onClick={() => setCorrectAnswersCount(correctAnswersCount + 1)}>Counter</button>
{showModal && (
<Modal
isOpen={showModal}
onClose={handleModalClose}
correctAnswersCount={correctAnswersCount}
lives={lives}
/>
)}
</div>
);
};
ReactDOM.createRoot(
document.getElementById("root")
).render(
<App />
);
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.2.0/umd/react.development.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.2.0/umd/react-dom.development.js"></script>