#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:
Две вещи:
- Если вы хотите, чтобы функция выполнялась при монтировании, вы используете
useEffect
пустой массив зависимостей, и вы можете безопасно игнорировать предупреждения linter в этом случае и только в этом случае. - Это (насколько я могу судить) не то, что вы хотите: вы хотите запустить его один раз при загрузке страницы, а затем никогда больше.
Если это так, то нет необходимости даже помещать ее в компонент. Извлеките ее в свою собственную вещь верхнего уровня:
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 с удовольствием, и да: помните, что любой код, который не является частью перехвата (и совсем немного того, что находится в перехвате), повторно запускается при каждом монтировании компонента, и компонент, скорее всего, будет перемонтироваться при каждом изменении состояния приложения (есть способычтобы изменить это, но мы уходим в сорняки).