Reputation: 105
I'm making a Trivia App in React. I shuffle the answers before rendering them as buttons and want to change the color of the buttons if the answer is right/wrong:
export default function QuestionCard({ question }){
const [isAnswered, setIsAnswered] = useState(false)
const initAnswers = [...question.incorrect_answers, question.correct_answer]
function shuffleAnswers(answers){
var j, x, i;
for(i = answers.length - 1; i > 0; i--){
j = Math.floor(Math.random()* (i + 1));
x = answers[i];
answers[i] = answers[j];
answers[j] = x;
}
return answers
}
const answers = shuffleAnswers(initAnswers);
const handleAnswerClick = (answer) => {
setIsAnswered(true)
}
return(
<Card>
{question.question}
<div>
{answers.map(answer => (
<Button
key={answer}
onClick={() => handleAnswerClick(answer)}
color={isAnswered && answer === question.correct_answer ?
'primary' :
isAnswered && answer !== question.correct_answer ? 'secondary' :
'default'}
>
{answer}
</Button>
))}
</div>
</Card>
)
}
When after clicking the Button, the Card 'forgets' the order from shuffleAnswers()
and returns the Buttons in their original order. Any
Upvotes: 0
Views: 38
Reputation: 202801
On each render cycle initAnswers
and answers
are both redeclared/redefined, thus when state is updated when a card is clicked on and state updates the component rerenders with new initAnswers
and shuffled answers
arrays.
You want to only initialize and shuffle the answers once per set of question answers. You could use an useEffect
hook to shuffle and store in state the answers
array, but IMO just memoizing the answers
array is better.
useMemo
hook with answers dependencies (i.e. only recompute when the incorrect and correct answers for question
prop update.shuffleAnswers
utility function out of the component, it doesn't have any state/prop/component dependencies and we don't want to redefine it each render cycle either.initAnswers
into the hook callback so it isn't an external dependency to computing answers
(otherwise you would need to memoize it as well!).Code:
function shuffleAnswers(answers) {
let j, x, i;
for (i = answers.length - 1; i > 0; i--) {
j = Math.floor(Math.random() * (i + 1));
x = answers[i];
answers[i] = answers[j];
answers[j] = x;
}
return answers;
}
function QuestionCard({ question }) {
const [isAnswered, setIsAnswered] = useState(false);
const answers = useMemo(() => {
const initAnswers = [
...question.incorrect_answers,
question.correct_answer
];
return shuffleAnswers(initAnswers);
}, [question.incorrect_answers, question.correct_answer]);
...
return (
...
);
}
You can use array destructuring assignment to swap two values in an array in a single line without a third variable
[answers[j], answers[i]] = [answers[i], answers[j]]
So your utility can reduce to
function shuffleAnswers(answers) {
let i, j;
for (i = answers.length - 1; i > 0; i--) {
j = Math.floor(Math.random() * (i + 1));
[answers[j], answers[i]] = [answers[i], answers[j]];
}
return answers;
}
Upvotes: 1