'Cannot properly concat to an array in ReactJS when using useState hook
I have a button that increments a vote counter whenever it is clicked, however I am having trouble implementing it. When I first click the Vote button, the function incrementVote is run and when I log my results to the console, I find that it prints out an empty array even though I concatenated the array. This problem only occurs for the first click, as subsequent clicks work as expected. Any help would be greatly appreciated.
import logo from './logo.svg';
import './App.css';
import Header from './Header';
import Feedback from './Feedback';
import { useState } from 'react';
import Statistics from './Statistics';
import AnecdoteBtn from './AnecdoteBtn';
import Phrase from './Phrase';
import { useEffect } from 'react';
function App() {
const anecdotes = [
'If it hurts, do it more often',
'Adding manpower to a late software project makes it later!',
'The first 90 percent of the code accounts for the first 10 percent of the development time...The remaining 10 percent of the code accounts for the other 90 percent of the development time.',
'Any fool can write code that a computer can understand. Good programmers write code that humans can understand.',
'Premature optimization is the root of all evil.',
'Debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as cleverly as possible, you are, by definition, not smart enough to debug it.',
'Programming without an extremely heavy use of console.log is same as if a doctor would refuse to use x-rays or blood tests when diagnosing patients'
]
const [selected, setSelected] = useState();
const [votes, setVotes] = useState([]);
const [finalVotes, setFinalVotes] = useState(0);
const [goodClick, setGoodClick] = useState(0);
const [badClick, setBadClick] = useState(0);
const [neutralClick, setNeutralClick] = useState(0);
const [average, setAverage] = useState(0);
// ----------------------------DOES NOT INCREMENT PROPERLY ON THE FIRST CLICK - AS IT PRINTS OUT AN EMPTY ARRAY---------------------------------------------------//
const incrementVote = () => {
console.log(votes.concat(selected));
var newArr = votes.concat(selected)
setVotes(newArr);
console.log(votes) //Prints out [] even though array is concatenated
if(votes.includes(selected)){
let numVotes = votes.filter((v) => (v === selected));
setFinalVotes(numVotes.length);
}
else {
setFinalVotes(0);
}
}
const setRandom = () => {
setSelected(Math.floor((Math.random() * 7) + 1));
}
useEffect(() => {
console.log("selected value has changed!")
setFinalVotes(0);
console.log("votes array is " + votes)
console.log("current selected value is " + selected)
let numVotes = votes.filter((v) => (v === selected));
setFinalVotes(numVotes.length);
}, [selected]);
const handleGoodClick = () => {
console.log("good btn clicked");
setGoodClick(goodClick + 1);
setAverage(average + 1);
}
const handleBadClick = () => {
console.log("bad btn clicked");
setBadClick(badClick + 1);
setAverage(average - 1);
}
const handleNeutralClick = () => {
console.log("neutral btn clicked");
setNeutralClick(neutralClick + 1);
setAverage(average + 0);
}
return (
<div className="App">
<Header />
<AnecdoteBtn handleClick = {setRandom} handleVoteClick = {incrementVote}/>
<Phrase index = {selected} anecdotes = {anecdotes} finalVotes = {finalVotes}/>
<Feedback handleGoodClick = {handleGoodClick} handBadClick = {handleBadClick} handleNeutralClick = {handleNeutralClick}/>
<Statistics goodClicks = {goodClick} badClicks = {badClick} neutralClicks = {neutralClick} average = {average}/>
</div>
);
}
export default App;
Solution 1:[1]
So javascript is an asynchronous language, what that means is that tasks that take time are executed after the execution of tasks that don't take time.
What's happening here is the setVotes(newArray) function is asynchronous and takes time to change the state variable votes, so the complier moves forward and prints the console log statement which leads to you seeing the empty array([]) in the console.
And before you start googling it again, no there is no way to make the compiler wait for the setVotes() function. What you can try is to log your state variable outside of the increment vote function, that way you can see it logging every time the view renders.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | Sampurna |
