简体   繁体   中英

Created a Quiz using React, trying to create bookmarking system where users can bookmark questions from quiz

Using React, I created a Quiz where users get questions and can select answers: https://codesandbox.io/s/quiz-1cf1m

I'm trying to add a bookmark functionality where the user can "bookmark" questions they want to get back to later. And once the quiz ends, can display the list of questions they bookmarked in the Result Summary (and hopefully click to get to that specific question).

In my Question Component, I added isBookMarked set to false and bookmarkedQuestions set to an empty array. Then created a handleBookmark function that toggles the bookmark icon.

class Question extends React.Component {
  state = {
    pick: false,
    correct: false,
    isBookmarked: false,
    bookmarkedQuestions: []
  };

  correctOrIncorrect = pick => {
    if (pick === this.state.pick) {
      return this.state.correct ? "correct" : "incorrect";
    } else if (
      pick === !this.state.correct &&
      this.props.currentQuestion.correctAnswer
    ) {
      return "correct";
    } else {
      return "clear";
    }
  };

  handleSelect = pick => {
    if (pick === this.props.currentQuestion.correctAnswer) {
      this.setState({ pick, correct: true });
    } else {
      this.setState({ pick });
    }
  };

  moveToNextQuestion = () => {
    this.setState({ pick: false, correct: false });
    this.props.nextQuestionHandler(this.state.correct);
  };

  handleBookmark = () => {
    console.log(this.props.currentQuestion);

    this.setState({
      isBookmarked: !this.state.isBookmarked
    });
  };

  render() {
    const { currentQuestion, shuffledAnswerChoices } = this.props;

    return (
      <div className={this.props.animation ? this.props.animation : null}>
        <span>
          <i
            id={currentQuestion}
            className={
              this.state.isBookmarked ? "fa fa-bookmark" : "fa fa-bookmark-o"
            }
            onClick={() => this.handleBookmark()}
          />
        </span>
        <p>{currentQuestion.text}</p>
        <ol type="A">
          {shuffledAnswerChoices.map((pick, idx) => {
            return (
              <li
                key={idx}
                className={this.state.pick ? this.correctOrIncorrect(pick) : null}
                onClick={() => this.handleSelect(pick)}
              >
                {pick}
              </li>
            );
          })}
        </ol>
        {this.state.pick && (
          <div>
            {this.state.correct ? (
              <p>
                <i>Correct!</i>
              </p>
            ) : (
              <p>
                <i>Incorrect</i>
              </p>
            )}
            <button className="next-btn" onClick={this.moveToNextQuestion}>
              Next
            </button>
          </div>
        )}
      </div>
    );
  }
}

export default Question;

I realized with the current code I have, when I bookmark the icon stays bookmarked throughout the quiz unless I click again to unbookmark it. I have added a console log and can grab the current question I am on. I'm stuck with being able to only bookmark the selected questions and once the user moves to the next question, it "resets." I am also trying to push the question into the empty bookmarkedQuestions array when bookmarked, which I want to display at the end of the quiz (but just realized that the Result Component is a sibling not a child of the Question Component, and therefore, cannot pass down as props...).

Any React Pros out there who can teach me how to approach this problem...?

Additional question expanding from this bookmark system : any approach suggestions on how to repeat all the incorrect questions again until all questions have been correctly answered with the summary showing how many questions were answered correctly the first time?

Definitely not a React Pro but I think I can be of help!

In my opinion, I think you should move the bookmark state to the Quiz level so that it persists for the duration of the quiz. This means passing down a function to the question from the quiz to control the state of the currently bookmarked questions. This also means that the 'isBookmarked' flag is passed down as a prop as well.

This may mean creating logic that overcomplicates what you are aiming to do.

LONG EXAMPLE (Some parts are overcomplicated for what it needs to do)- feel free to not use any of this, but I got into it so felt like adding it: https://codesandbox.io/s/quiz-x8wpv?fontsize=14&hidenavigation=1&theme=dark

I messed around with your code and created an example of how it would work. The data structure I chose for the bookmarks is a an object that has top level key of the quiz, [quizNumber], the value being another object with keys being each question. Which would then have a boolean flag which will indicate if bookmarked or not. To achieve this adding a field id for each question would be helpful

To avoid storing lots of data I omitted the actual question from the data structure as it can then be looked up with the quizNumber and question ID.

So I took the liberty of updating the questions to match this style

  {
        qID: 1,
        text: "What is the capital of United States?",
        correctAnswer: "Washington, D.C.",
        incorrectAnswers: ["Philadelphia", "Annapolis", "New York City"]
      }

So an example of what the structure of the bookmarkedQuestions object could be is,

{
 '0':{1:true},
 '2':{1:false, 2:true , 3:true}
}

This example would mean quiz 0 question 1 has been bookmarked as well as quiz 2 question 2 and 3. If the key does not exist or is false it can be assumed that the question has not been bookmarked.

To set this a function at the quiz level would need to be created. The function I created is most likely way overcomplicated for what it needs to do but is as follows

  bookmarkQuestion = () => {
    const { currentQuiz, numOfQuestions, quizQuestionBookmarks } = this.state;
    const { questions } = quizzes[currentQuiz];
    const { qID } = questions[numOfQuestions];
    // Can be simplified by creating a function to update nested values, or by using lodash
    // The destructuring is required so other parts of the object are not modified when updated deeper keys
    this.setState({
      quizQuestionBookmarks: {
        ...quizQuestionBookmarks,
        [currentQuiz]: {
          ...quizQuestionBookmarks[currentQuiz],
          [qID]: quizQuestionBookmarks[currentQuiz]
            ? !quizQuestionBookmarks[currentQuiz][qID]
            : true
        }
      }
    });
  };

The idea is that this can now be used on the currentQuizQuestions to get the list of questions that are currently bookmarked.

  getBookmarkedQuestions = () => {
    const { currentQuiz, quizQuestionBookmarks } = this.state;
    const { questions } = quizzes[currentQuiz];
    const boorkmarkedQuestions = questions.filter(question =>
      quizQuestionBookmarks[currentQuiz]
        ? quizQuestionBookmarks[currentQuiz][question.qID]
        : false
    );
    return boorkmarkedQuestions;
  };

In my example I also pass the bookmarkedQuestions to the Results page as a prop to demonstrate how the data can be passed to it to be used.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM