sbaden
sbaden

Reputation: 565

How do I pull data from object in array as state is being updated in React with hooks?

This is part of a previous post: What's the React best practice for getting data that will be used for page render?

I'm adding objects to an array with useState([]). Each object has a firstName and a lastName value. I want to add up all of the length of all the first and last names to get a total character count so that I can then get the average name length for each.

What I have currently is giving me the wrong values: firstName (100) lastName (106)

The values should be 6 & 7

  const [players, setPlayers] = useState([]);
  const [firstNameCount, setFirstNameCount] = useState(0);
  const [lastNameCount, setLastNameCount] = useState(0);

  useEffect(() => {
    setPlayers([]);

    teams.forEach(teamId => {
      axios.defaults.headers.common['Authorization'] = authKey;

      axios.get(endPoints.roster + teamId)
        .then((response) => {
          let teamPlayers = response.data.teamPlayers;
          console.log(response.data.teamPlayers)
          setPlayers(prevPlayers => [...prevPlayers, ...teamPlayers]);
        })
        .catch((error) => {
          console.log(error);
        })
    });
  }, [teams]);

  useEffect(() => {
     players.forEach(player => {
            setFirstNameCount(prevCount => prevCount + player.firstName.length);
            setLastNameCount(prevCount => prevCount + player.lastName.length);
          })
  }, [players]);

If I change the code to this

useEffect(() => {
    setPlayers([]);

    teams.forEach(teamId => {
      axios.defaults.headers.common['Authorization'] = authKey;

      axios.get(endPoints.roster + teamId)
        .then((response) => {
          let teamPlayers = response.data.teamPlayers;
          console.log(response.data.teamPlayers)
          setPlayers(prevPlayers => [...prevPlayers, ...teamPlayers]);

          teamPlayers.forEach(player => {
            setFirstNameCount(prevCount => prevCount + player.firstName.length);
            setLastNameCount(prevCount => prevCount + player.lastName.length);
          })
        })
        .catch((error) => {
          console.log(error);
        })
    });
  }, [teams]);

It slows down the render considerably and I get: firstName (7), lastName (7), which is still wrong.

Upvotes: 0

Views: 294

Answers (2)

ray
ray

Reputation: 27255

I'd recommend you move your team/player loading out of your component and into a store, separate from the component, but in any case, why put the lengths in state? Why not just compute it from the player state when you need it?

const sum = values => values.reduce((r, v) => r + v, 0);
const avg = values => sum(values) / values.length;

const avgFirst = avg(players.map(p => p.firstName.length));
const avgLast = avg(players.map(p => p.lastName.length));

I realize that this would cause it to be recomputed on every render, but unless your list of players is massive I would expect this to be reasonably performant.

Upvotes: 1

rzwnahmd
rzwnahmd

Reputation: 1082

Well instead of setting state for every increment what you could do is add up all the lengths of firstnames and lastnames and then set the state like this:

useEffect(() => {
    setPlayers([]);

    teams.forEach(teamId => {
      axios.defaults.headers.common['Authorization'] = authKey;

      axios.get(endPoints.roster + teamId)
        .then((response) => {
          let teamPlayers = response.data.teamPlayers;
          console.log(response.data.teamPlayers)
          setPlayers(prevPlayers => [...prevPlayers, ...teamPlayers]);

          let firstNameCount = 0;
          let lastNameCount = 0;

          teamPlayers.forEach(player => {
            firstNameCount += player.firstName.length;
            lastNameCount += player.lastName.length;
          })
          setFirstNameCount(prevCount => prevCount + firstNameCount);
          setLastNameCount(prevCount => prevCount + lastNameCount);
        })
        .catch((error) => {
          console.log(error);
        })
    });
  }, [teams]);

Let me know if it helps :)

Upvotes: 0

Related Questions