I need to update the player state on click (setPlayerState) but it's not being doing on time. I mean, it updates later on, so I get the first state only after the second click when the state has changed again. Does anyone know how to solve this?
const [player, setPlayerState] = useState({
name,
assertions: 0,
score: 0,
gravatarEmail,
});
function handleClick({ target }) {
const id = target.getAttribute('data-testid').includes('correct');
let points = 0;
if (id) {
const level = questions[index].difficulty;
if (level === 'hard') points = THREE;
else if (level === 'medium') points = TWO;
else points = ONE;
setPlayerState({
...player,
assertions: player.assertions 1,
score: player.score (TEN (timer * points)),
});
}
}
CodePudding user response:
Some points in your codes stand out
- You reference
index
inside of yourif(id) ...
conditional butindex
is not set. ONE
,TWO
,THREE
, andTEN
are not set.- Question scoring logic is tangled with state update logic
Here is a minimal complete example that fixes these things and shows a better way to write onClick
. Run the code example and click the question buttons to see how assertions
and score
updates respectively.
function App({ questions = [] }) {
const [player, setPlayer] = React.useState({
name: "Hanna",
assertions: 0,
score: 0,
})
function calcScore(difficulty) {
switch (difficulty) {
case "hard": return 3
case "medium": return 2
default: return 1
}
}
function onClick(question) { return event => {
if (event.target.getAttribute("data-test-id").includes("correct"))
setPlayer({
...player,
assertions: player.assertions 1,
score: player.score calcScore(question.difficulty)
})
}}
return <div>
{questions.map((q, key) =>
<button key={key} data-test-id="correct" onClick={onClick(q)}>
{q.ask}
</button>
)}
<pre>{JSON.stringify(player, null, 2)}</pre>
</div>
}
const questions = [
{ ask: "What is?", difficulty: "easy" },
{ ask: "How come?", difficulty: "medium" },
{ ask: "Seriously?", difficulty: "hard" }
]
ReactDOM.render(<App questions={questions} />, document.querySelector("#app"))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.14.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.14.0/umd/react-dom.production.min.js"></script>
<div id="app"></div>
CodePudding user response:
Add a click state and useEffect
to respond to it's change. It effectively forces two passes for the re-render.
Also try setPlayerState with previous value parameter i.e setPlayerState(player =>
which sometimes helps when updates are not noticed.
const [player, setPlayerState] = useState({
name,
assertions: 0,
score: 0,
gravatarEmail,
});
const [clickTarget, setClickTarget] = useState(null)
function handleClick({ target }) {
setClickTarget(target )
}
useEffect(() => {
if (!clickTarget) return
const id = clickTarget.getAttribute('data-testid').includes('correct');
let points = 0;
if (id) {
const level = questions[index].difficulty;
points = (level === 'hard') ? THREE :
(level === 'medium') ? TWO : ONE;
setPlayerState(player => ({
...player,
assertions: player.assertions 1,
score: player.score (TEN (timer * points)),
}))
}
setClickTarget(null)
}, [clickTarget])