I have this React code:
import React, { useState, useEffect } from "react";
import axios from "axios";
function App() {
const [players, setPlayers] = useState([]);
// Get all Players
const getAllPlayersUrl = "http://localhost:5087/api/GetAllPlayers";
useEffect(() => {
axios.get(getAllPlayersUrl).then((response) => {
setPlayers(response.data);
});
}, []);
const [playerCount, setPlayerCount] = useState(players.length);
return (
<div>
<p>{`This is how many there are: ${playerCount}`}</p>
</div>
);
}
export default App;
I want to print how many initial players using playerCount
variable. However it says it's zero:
This is how many there are: 0
If I instead print players.length
, it would output the correct number:
<p>{`This is how many there are: ${players.length}`}</p>
This is how many there are: 9
Even if I remove dependency array to keep rendering, playerCount
still wont update:
useEffect(() => {
axios.get(getAllPlayersUrl).then((response) => {
setPlayers(response.data);
});
});
I wonder why the useState
is not working? Is there something I am missing in my code? I would like to know what it is, thank you!
******* EDIT ************
Honestly not sure why this received downvotes. If this is a bad question, I would like to know why. If this question has been asked before, then I would like to see the similar question. Really no need to downvote.
CodePudding user response:
A good rule of thumb with state (and props) is to avoid duplicating state values when a value can be determined entirely by another. Otherwise, you can run into issues like these, where keeping multiple states in sync can be more challenging than it needs to be.
Here, you set the initial value of playerCount
when the component mounts:
const [playerCount, setPlayerCount] = useState(players.length);
And the component mounts only once - and at that time, players
is the empty array - so playerCount
becomes 0, and because you never call setPlayerCount
, it always remains 0.
While you could fix it by calling setPlayerCount
inside your .then
, a better approach would be to either calculate the player count from the players
state only when needed:
function App() {
const [players, setPlayers] = useState([]);
const getAllPlayersUrl = "http://localhost:5087/api/GetAllPlayers";
useEffect(() => {
axios.get(getAllPlayersUrl).then((response) => {
setPlayers(response.data);
});
}, []);
return (
<div>
<p>{`This is how many there are: ${players.length}`}</p>
</div>
);
}
Or, if you really had to, to memoize the count depending on the players
array (without creating additional state).
function App() {
const [players, setPlayers] = useState([]);
const playerCount = useMemo(() => players.length, [players]);
const getAllPlayersUrl = "http://localhost:5087/api/GetAllPlayers";
useEffect(() => {
axios.get(getAllPlayersUrl).then((response) => {
setPlayers(response.data);
});
}, []);
return (
<div>
<p>{`This is how many there are: ${playerCount}`}</p>
</div>
);
}