Как запустить функцию только один раз в react

#javascript #reactjs #ecmascript-6 #react-hooks

#javascript #reactjs #ecmascript-6 #реагирующие хуки

Вопрос:

Я работаю над простым приложением для викторины. У меня есть локальный массив с вопросами, который каждый раз находится в случайном порядке. Функция, которая перетасовывает массив в другом порядке, запускается при каждом запуске handleAnswerOptionClick .

Как я могу запустить функцию arrayShuffle один раз?

Я предполагаю, что я мог бы использовать useEffect для этого, но я не заставил его работать. Он жалуется на то, что у него нет разных зависимостей.

  useEffect(() => {
    arrayShuffle(questions);
  }, []);
  

Код: CodeSandox

QuizContainer:

 import React, { useState, useEffect } from "react";

export const QuizContainer = () => {
  const questions = [
    {
      questionText: "What is the capital of France?",
      answerOptions: [
        { option: "New York", isCorrect: false },
        { option: "London", isCorrect: false },
        { option: "Paris", isCorrect: true },
        { option: "Dublin", isCorrect: false }
      ]
    },
    {
      questionText: "Who is CEO of Tesla?",
      answerOptions: [
        { option: "Jeff Bezos", isCorrect: false },
        { option: "Elon Musk", isCorrect: true },
        { option: "Bill Gates", isCorrect: false },
        { option: "Tony Stark", isCorrect: false }
      ]
    },
    {
      questionText: "The iPhone was created by which company?",
      answerOptions: [
        { option: "Apple", isCorrect: true },
        { option: "Intel", isCorrect: false },
        { option: "Amazon", isCorrect: false },
        { option: "Microsoft", isCorrect: false }
      ]
    },
    {
      questionText: "How many Harry Potter books are there?",
      answerOptions: [
        { option: "1", isCorrect: false },
        { option: "4", isCorrect: false },
        { option: "6", isCorrect: false },
        { option: "7", isCorrect: true }
      ]
    }
  ];

  const [currentQuestion, setCurrentQuestion] = useState(0);
  const [showScore, setShowScore] = useState(false);
  const [score, setScore] = useState(0);

  // Event handlers
  const handleAnswerOptionClick = (isCorrect) => {
    if (isCorrect) {
      setScore(score   1);
    }

    const nextQuestion = currentQuestion   1;
    nextQuestion < questions.length
      ? setCurrentQuestion(nextQuestion)
      : setShowScore(true);
  };

  // ShuffleArray
  const arrayShuffle = function (arr) {
    let newPos, temp;
    for (let i = arr.length - 1; i > 0; i--) {
      newPos = Math.floor(Math.random() * (i   1));
      temp = arr[i];
      arr[i] = arr[newPos];
      arr[newPos] = temp;
    }
    return arr;
  };

  const newArray = arrayShuffle(questions);
  console.log(newArray);

  return (
    <>
      <h1>QuizContainer</h1>
      {showScore ? (
        <p>
          You scored {score} out of {questions.length}
        </p>
      ) : (
        <>
          <p>
            Question {currentQuestion   1}/{questions.length}
          </p>

          <p>{questions[currentQuestion].questionText}</p>

          <div>
            {questions[currentQuestion].answerOptions.map(
              (answerOption, index) => (
                <div key={index}>
                  <button
                    onClick={() =>
                      handleAnswerOptionClick(answerOption.isCorrect)
                    }
                  >
                    {answerOption.option}
                  </button>
                </div>
              )
            )}
          </div>
        </>
      )}
    </>
  );
};
  

Просто спросите, нужно ли мне уточнить, и считаете ли вы, ребята, что я правильно подхожу к этому.

Заранее спасибо,

Эрик

Ответ №1:

Две вещи:

  1. Если вы хотите, чтобы функция выполнялась при монтировании, вы используете useEffect пустой массив зависимостей, и вы можете безопасно игнорировать предупреждения linter в этом случае и только в этом случае.
  2. Это (насколько я могу судить) не то, что вы хотите: вы хотите запустить его один раз при загрузке страницы, а затем никогда больше.

Если это так, то нет необходимости даже помещать ее в компонент. Извлеките ее в свою собственную вещь верхнего уровня:

   const questions = [
    {
      questionText: "What is the capital of France?",
      answerOptions: [
        { option: "New York", isCorrect: false },
        { option: "London", isCorrect: false },
        { option: "Paris", isCorrect: true },
        { option: "Dublin", isCorrect: false }
      ]
    },
    {
      questionText: "Who is CEO of Tesla?",
      answerOptions: [
        { option: "Jeff Bezos", isCorrect: false },
        { option: "Elon Musk", isCorrect: true },
        { option: "Bill Gates", isCorrect: false },
        { option: "Tony Stark", isCorrect: false }
      ]
    },
    {
      questionText: "The iPhone was created by which company?",
      answerOptions: [
        { option: "Apple", isCorrect: true },
        { option: "Intel", isCorrect: false },
        { option: "Amazon", isCorrect: false },
        { option: "Microsoft", isCorrect: false }
      ]
    },
    {
      questionText: "How many Harry Potter books are there?",
      answerOptions: [
        { option: "1", isCorrect: false },
        { option: "4", isCorrect: false },
        { option: "6", isCorrect: false },
        { option: "7", isCorrect: true }
      ]
    }
  ];

  const arrayShuffle = function (arr) {
    let newPos, temp;
    for (let i = arr.length - 1; i > 0; i--) {
      newPos = Math.floor(Math.random() * (i   1));
      temp = arr[i];
      arr[i] = arr[newPos];
      arr[newPos] = temp;
    }
    return arr;
  };

  const newArray = arrayShuffle(questions);

  export const QuizContainer = () => {
    /* rest of component */
  }
  

Если вы хотите повторно запустить ее, но хотите иметь контроль над этим, вы можете перевести ее в состояние:

 export const QuizContainer = () => {
  const [shuffled, setShuffled] = useState(arrayShuffle(questions));
  const reShuffle = () => setShuffled(arrayShuffle(questions));
  /* rest of the component */
}
  

Но в целом не загромождайте определения компонентов статическими константами, которые останутся неизменными для всего просмотра страницы.

Комментарии:

1. Что касается (1): пока questions не изменится, нет ничего плохого в добавлении его в массив зависимостей.

2. @JonasWilms абсолютно, но, как я уверен, вы знаете, мы получаем 50 вопросов в день о том, что линтер жалуется на пустой массив зависимостей.

3. А, понятно! Это было просто, потому что это было внутри компонента. Я буду изучать зависимость от эффекта использования дальше, но спасибо, что указали мне правильное направление. // Эрик @JaredSmith

4. @erikos93 с удовольствием, и да: помните, что любой код, который не является частью перехвата (и совсем немного того, что находится в перехвате), повторно запускается при каждом монтировании компонента, и компонент, скорее всего, будет перемонтироваться при каждом изменении состояния приложения (есть способычтобы изменить это, но мы уходим в сорняки).