Implementing an auto-submit feature for a ReactJS quiz with a timer: how can I count the correct answers after the timer stops?

huangapple go评论58阅读模式
英文:

Implementing an auto-submit feature for a ReactJS quiz with a timer: how can I count the correct answers after the timer stops?

问题

以下是您要翻译的内容:

"Auto submit not working how it should.

The code is working fine if I manually submit the quiz at the end, however if the time runs out the score will always be 0/3 which is not correct."

请注意,这是您提供的英文原文。如果需要中文翻译,请提供更多上下文或具体要翻译的部分。

英文:

Auto submit not working how it should.

The code is working fine if I manually submit the quiz at the end, however if the time runs out the score will always be 0/3 which is not correct.

import React, { useEffect, useState } from 'react';
import { makeStyles } from '@mui/styles';
import { Container, Typography, Button, Checkbox, Dialog, DialogTitle, DialogContent, DialogActions, } from '@mui/material';
const useStyles = makeStyles((theme) => ({
root: {
background: '#ADD8E6',
minHeight: '100vh',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
},
appContainer: {
background: '#fff',
borderRadius: 10,
padding: 30,
},
delimiter: {
color: '#001e4d',
borderBottom: '1px solid #333',
paddingBottom: 13,
marginBottom: 8,
},
question: {
fontSize: 18,
color: '#001e4d',
fontWeight: 600,
paddingTop: 3,
},
answerContainer: {
display: 'flex',
flexDirection: 'column',
alignItems: 'flex-start',
marginTop: 20,
},
answerText: {
fontSize: 18,
color: '#001e4d',
fontWeight: 500,
},
answerCheckbox: {
marginBottom: 10,
},
answerButton: {
background: '#fff',
color: '#222',
fontWeight: 500,
width: '100%',
border: '1px solid #222',
padding: 10,
margin: '10px 0',
textAlign: 'left',
borderRadius: 4,
cursor: 'pointer',
'&:hover:not($displayed)': {
background: '#222',
color: '#fff',
},
'&:disabled': {
cursor: 'no-drop',
},
},
nextButton: {
background: '#001e4d',
color: '#fff',
fontWeight: 500,
width: 120,
border: 0,
padding: 10,
margin: '20px 0 0 auto',
borderRadius: 4,
cursor: 'pointer',
display: 'none',
},
previousButton: {
backgroundColor: '#ffffff',
color: '#222',
fontWeight: 500,
},
timer: {
marginLeft: '10rem',
marginTop: '0.5rem',
},
redTimer: {
color: 'red',
},
}));
const questions = [
{
question: "What is Lorem Ipsum?",
selectedAnswerIndex: -1,
answers: [
{ text: "Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s", correct: false },
{ text: "Lorem Ipsum has been the industry's standard dummy text ever since the 1500s", correct: true },
{ text: "Lorem Ipsum", correct: false },
{ text: "Lorem Ipsum is simply dummy text of the printing and typesetting industry", correct: false },
]
},
{
question: "What is Lorem Ipsum?",
answers: [
{ text: "1", correct: false },
{ text: "2", correct: true },
{ text: "3", correct: false },
{ text: "4", correct: false },
]
},
{
question: "What is Lorem Ipsum?",
answers: [
{ text: "1", correct: false },
{ text: "2", correct: true },
{ text: "3", correct: false },
{ text: "4", correct: false },
]
},
];
const JoinQuiz = () => {
const classes = useStyles();
const [currentQuestionIndex, setCurrentQuestionIndex] = useState(0);
const [score, setScore] = useState(0);
const [selectedAnswerIndex, setSelectedAnswerIndex] = useState(-1);
const [timer, setTimer] = useState(5);
const [showDialog, setShowDialog] = useState(false);
const [dialogMessage, setDialogMessage] = useState('');
const [isSubmitted, setIsSubmitted] = useState(false);
const [userAnswers, setUserAnswers] = useState(new Array(questions.length).fill(-1));
const currentQuestion = questions[currentQuestionIndex];
useEffect(() => {
let timerId;
if (!isSubmitted) { // Only start the timer if quiz is not submitted
timerId = setInterval(() => {
setTimer((prevTimer) => {
if (prevTimer === 0) {
clearInterval(timerId);
handleQuizSubmission(); // Automatically submit quiz when timer reaches 0
return 0;
}
return prevTimer - 1;
});
}, 1000);
}
return () => clearInterval(timerId); // Clear the timer when component unmounts or quiz is submitted
}, [isSubmitted]);
const handleNextQuestion = () => {
const nextQuestionIndex = currentQuestionIndex + 1;
if (nextQuestionIndex < questions.length) {
setCurrentQuestionIndex(nextQuestionIndex);
setSelectedAnswerIndex(-1); // Reset selected answer
} else {
handleQuizSubmission();
}
};
const handlePreviousQuestion = () => {
const previousQuestionIndex = currentQuestionIndex - 1;
if (previousQuestionIndex >= 0) {
setCurrentQuestionIndex(previousQuestionIndex);
}
};
const handleQuizSubmission = () => {
let totalScore = 0;
for (let i = 0; i < questions.length; i++) {
const question = questions[i];
const userAnswerIndex = userAnswers[i];
if (userAnswerIndex !== -1) {
if (question.answers[userAnswerIndex]?.correct) {
totalScore += 1;
}
}
}
const scorePercentage = (totalScore / questions.length) * 100;
const message = `Quiz submitted! Score: ${totalScore}/${questions.length} (${scorePercentage}%)`;
setShowDialog(true);
setDialogMessage(message);
setIsSubmitted(true);
};
const handleAnswerSelect = (index, correct) => {
if (!isSubmitted) {
setSelectedAnswerIndex(index);
const updatedUserAnswers = [...userAnswers];
updatedUserAnswers[currentQuestionIndex] = index;
setUserAnswers(updatedUserAnswers);
if (correct) {
setScore(score + 1);
}
}
};
useEffect(() => {
// Update the selected answer index when the current question changes
if (currentQuestionIndex >= 0) {
setSelectedAnswerIndex(userAnswers[currentQuestionIndex]);
}
}, [currentQuestionIndex, userAnswers]);
const handleDialogClose = () => {
setShowDialog(false);
};
const formatTime = (seconds) => {
const minutes = Math.floor(seconds / 60);
const secondsRemaining = seconds % 60;
return `${minutes.toString().padStart(2, '0')}:${secondsRemaining.toString().padStart(2, '0')}`;
};
return (
<div className={classes.root}>
<Container className={classes.appContainer} maxWidth="lg">
<div className={classes.quiz}>
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
<Typography variant="h4" component="h1" >
Simple Quiz
</Typography>
<Typography
variant="h6"
className={`${classes.timer} ${timer <= 120 && !isSubmitted ? classes.redTimer : ''}`}
>
Timer: {formatTime(timer)}
</Typography>
</div>
<div className={classes.delimiter}></div>
<Typography variant="h3" className={classes.question}>
{currentQuestion.question}
</Typography>
<div className={classes.answerContainer}>
{currentQuestion.answers.map((answer, index) => (
<div key={index} className={classes.answerText}>
<Checkbox
className={classes.answerCheckbox}
checked={selectedAnswerIndex === index}
onChange={() => handleAnswerSelect(index, answer.correct)}
disabled={isSubmitted} // Disable checkboxes when quiz is submitted
/>
{String.fromCharCode(97 + index)})  {answer.text}
</div>
))}
</div>
<div style={{ display: 'flex', justifyContent: 'flex-end' }}>
{currentQuestionIndex > 0 && (
<Button
sx={{ marginTop: '0.3rem', marginBottom: '0.3rem', marginRight: '1rem' }}
style={{ backgroundColor: '#ffffff', color: '#222', fontWeight: 500 }}
className={classes.previousButton}
variant="contained"
onClick={handlePreviousQuestion}
disabled={isSubmitted}
>
Previous
</Button>
)}
<Button
sx={{ marginTop: '0.3rem', marginBottom: '0.3rem', marginLeft: '4rem' }}
style={{ backgroundColor: '#4c9deb' }}
className={classes.nextButton}
variant="contained"
color="primary"
id={currentQuestionIndex === questions.length - 1 ? 'Submit-btn' : 'Next-btn'}
onClick={handleNextQuestion}
disabled={selectedAnswerIndex === -1 || isSubmitted} // Disable button when quiz is submitted
>
{currentQuestionIndex === questions.length - 1 ? 'Submit' : 'Next'}
</Button>
<Dialog open={showDialog} onClose={handleDialogClose}>
<DialogTitle>Quiz Results</DialogTitle>
<DialogContent className={classes.dialogContent}>
<Typography variant="h6" gutterBottom>
{dialogMessage}
</Typography>
<Typography variant="body1" gutterBottom>
Thank you for taking the quiz!
</Typography>
</DialogContent>
<DialogActions>
<Button onClick={handleDialogClose} color="primary" variant="contained">
Close
</Button>
</DialogActions>
</Dialog>
</div>
</div>
</Container>
</div>
);
};
export default JoinQuiz;

I'm expecting that when the time ends the answers given so far will count to the final score and the unanswered questions will be count as incorrect.

答案1

得分: 1

这是因为您没有将 userAnswers 添加到 useEffect 的依赖列表中。但是,在将 userAnswers 添加到依赖列表后,它不会按您期望的那样工作,因为它将在每次 userAnswers 更改时执行。

我认为这不是一个好的解决方案,但它可以解决您的问题。

useEffect(() => {
    let timerId;
    if (!isSubmitted) { // 仅在测验未提交时启动计时器
        timerId = setInterval(() => {
            setTimer((prevTimer) => {
                if (prevTimer === 0) {
                    clearInterval(timerId);

                    setUserAnswers((answers) => {
                        handleQuizSubmission(answers); // 当计时器达到0时自动提交测验
                        return answers;
                    });
                    return 0;
                }
                return prevTimer - 1;
            });
        }, 1000);
    }
    return () => clearInterval(timerId); // 在组件卸载或测验提交时清除计时器
}, [isSubmitted]);

const handleQuizSubmission = (answers = userAnswers) => {
    let totalScore = 0;
    for (let i = 0; i < questions.length; i++) {
        const question = questions[i];
        const userAnswerIndex = answers[i];
        if (userAnswerIndex !== -1) {
            if (question.answers[userAnswerIndex]?.correct) {
                totalScore += 1;
            }
        }
    }
    const scorePercentage = (totalScore / questions.length) * 100;
    const message = `Quiz submitted! Score: ${totalScore}/${questions.length} (${scorePercentage}%)`;
    setShowDialog(true);
    setDialogMessage(message);
    setIsSubmitted(true);
};

希望对您有所帮助。

英文:

This is because you haven't added userAnswers to dependency list of useEffect.
But after add userAnswers to dependency list, it couldn't work as you expected as it will be executed whenever userAnswers changed.

I think it's not good solution but it can solve your problem.

 useEffect(() =&gt; {
let timerId;
if (!isSubmitted) { // Only start the timer if quiz is not submitted
timerId = setInterval(() =&gt; {
setTimer((prevTimer) =&gt; {
if (prevTimer === 0) {
clearInterval(timerId);
setUserAnswers((answers) =&gt; {
handleQuizSubmission(answers); // Automatically submit quiz when timer reaches 0
return answers
})
return 0;
}
return prevTimer - 1;
});
}, 1000);
}
return () =&gt; clearInterval(timerId); // Clear the timer when component unmounts or quiz is submitted
}, [isSubmitted]);
const handleQuizSubmission = (answers = userAnswers) =&gt; {
let totalScore = 0;
for (let i = 0; i &lt; questions.length; i++) {
const question = questions[i];
const userAnswerIndex = answers[i];
if (userAnswerIndex !== -1) {
if (question.answers[userAnswerIndex]?.correct) {
totalScore += 1;
}
}
}
const scorePercentage = (totalScore / questions.length) * 100;
const message = `Quiz submitted! Score: ${totalScore}/${questions.length} (${scorePercentage}%)`;
setShowDialog(true);
setDialogMessage(message);
setIsSubmitted(true);
};

huangapple
  • 本文由 发表于 2023年5月21日 01:04:05
  • 转载请务必保留本文链接:https://go.coder-hub.com/76296406.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定