I'm fetching data from an API, and I map over it in order to generate 5 groups of radio buttons. Everything works fine, except for the fact that when I click on any of the input radio buttons, it seems to re-render the entire component or something, as you can see below (this is only one of the groups, but it happens and affects all of them):
Here's the code:
import { useState } from "react";
import { nanoid } from "nanoid";
export default function Question({ data }) {
const [userSelections, setUserSelections] = useState({
question0: "",
question1: "",
question2: "",
question3: "",
question4: "",
})
function handleSelection(event) {
const {name, value} = event.target
setUserSelections(prevSelection => {
return {
...prevSelection,
[name]: value
}
})
}
const renderQuestionBlocks = data.map(({question, correct_answer, incorrect_answers, uniqueQuestionId}, questionIndex) => {
const allAnswers = [...incorrect_answers, correct_answer].sort((a, b) => 0.5 - Math.random())
data.uniqueQuestionId = nanoid()
return (
<form className={`question--container question${questionIndex}`} key={data.uniqueQuestionId}>
<p>{question.replace(/"/g, '"').replace(/'/g, "'")}</p>
{allAnswers.map((answer, answerIndex) => {
data[questionIndex].uniqueAnswerId = nanoid()
return (
<div
key={data[questionIndex].uniqueAnswerId}
className={`choice--container question${questionIndex}`}
>
<input
type="radio"
name={`question${questionIndex}`}
id={`question${questionIndex}option${answerIndex}`}
value={answer}
checked={userSelections[`question${questionIndex}`] === answer}
onChange={handleSelection}
/>
<label htmlFor={`question${questionIndex}option${answerIndex}`}>{answer.replace(/"/g, '"').replace(/'/g, "'")}</label>
</div>
)
})}
</form>
)
})
return (
<div className="questions--container">
{renderQuestionBlocks}
<button>Check answers</button>
</div>
)
}
CodePudding user response:
it seems to re-render the entire component or something
Updating state re-renders a component. This is expected and correct (and fundamentally necessary) behavior in React. But... What does your component do when it renders?
It randomly sorts the values:
const allAnswers = [...incorrect_answers, correct_answer].sort((a, b) => 0.5 - Math.random())
It sounds like either that sort shouldn't be random, or should be randomized outside the scope of this component, or that randomization should itself be persisted in state so you can re-use it on each render.
For example, if you just need a random number for the component then you can generate one and keep it in state:
const [randonNumber] = useState(Math.random());
This will randomly generate a number, but won't re-generate one on every render. Then you can consistently use the same randomly generated number:
const allAnswers = [...incorrect_answers, correct_answer].sort((a, b) => 0.5 - randonNumber)