Home > database >  Why is simple useState initialization is not working?
Why is simple useState initialization is not working?

Time:10-16

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>
  );
}
  • Related