0

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)})&nbsp; {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.

Ausku
  • 17
  • 3

1 Answers1

1

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(() => {
        let timerId;
        if (!isSubmitted) { // Only start the timer if quiz is not submitted
          timerId = setInterval(() => {
            setTimer((prevTimer) => {
              if (prevTimer === 0) {
                clearInterval(timerId);

                setUserAnswers((answers) => {
                  handleQuizSubmission(answers); // Automatically submit quiz when timer reaches 0
                  return answers
                })
                return 0;
              }
              return prevTimer - 1;
            });
          }, 1000);
        }
        return () => clearInterval(timerId); // Clear the timer when component unmounts or quiz is submitted
      }, [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);
  };
Andy Lee
  • 116
  • 2