Search code examples
reactjstypescriptreact-hooksuse-effectuse-state

How to update useState() when value changes in React


I am having trouble on changing count value in React.

I have dynamic set, and I want to render the size of set whenever it changes!

So I want to keep update count variable to ratedSet.size whenever the ratedSet's size changes.

I get it that useSet wont render again until whenever the thing ends so I tried to use useEffect and it still won't work. I think i am not using it correctly.

How can I show the size of set whenever it changes ?

Below are my Code. I added few comments on what i tried to do:

import React, { useEffect, useState } from "react";
import BoardCardMain from "./component/BoardCardSurvey";
//other import statements

export default function Survey() {
  const [gameList, setGameList] = useState<Game[]>([]);
  const [count, setCount] = useState(0); //This is the count variable that I want to keep update
  const [width] = useState(window.innerWidth);
  let ratedSet = new Set(); //this is the set that I want to keep track of
  let cnt = 0;

  useEffect(() => {
    setGameList(tempData.gameList);
  }, []);

// I tried to do this but im not even close to answer...
  useEffect(() => {
    setCount(ratedSet.size);
  }, [ratedSet.size]);

  //This is the part I am changing the size of set dynamically
  //I am getting gameNo and score from child tsx, and am updating the set based on it
  const countHandler = (ratedGameNo: number, score: number) => {
    if (score > 0) {
      ratedSet.add(ratedGameNo);
    } else {
      ratedSet.forEach((item) => {
        if (item === ratedGameNo) {
          ratedSet.delete(item);
        }
      });
    }
//So here, if I console.log(ratedSet.size), it gives me correct values.
//but if i do setCount(ratedSet.size) it only updates once and never change again
  };
  return (
    <>

      SIZE IS : {count}
      SIZE IS : {ratedSet.size}
      None of them works 
     
      <Box> I am updating my set with below map : </Box>

        <Container style={{ marginTop: 20, padding: 10 }}>
          <Grid container spacing={2}>
            {gameList.map((game) => (
              <BoardCardMain
                key={game.gameNo}
                game={game}
                parentCallback={countHandler}
              ></BoardCardMain>
            ))}
          </Grid>
        </Container>
    </>
  );
}
const tempData = {
  gameList: [
    {
      gameNo: 1,
      gameImg:
        "https:/",
    },
    {
      gameNo: 12,
      gameImg:
        "https://",
    },
    {
      gameNo: 2,
      gameImg:
        "https://r",
    },
  ],
};


Solution

  • Because ratedSet is not a state of the component. Mutating the set will NOT cause the component re-render. So the useEffect(() => {}, [ratedSet.size]) hook will not execute again.

    I think there are two solutions:

    1. Force updates the component when any changes are made to the ratedSet.
    const forceUpdate: () => void = React.useState()[1].bind(null, {});
    
    1. Maintain the ratedSet as a state, you can create a custom hook like useSet. (Recommend)

    And you declare the ratedSet inside function component may be wrong. Because every time component rendering, you will create a new one.