Home > Software design >  UseEffect multiple re-renders
UseEffect multiple re-renders

Time:10-24

I'm trying to do a battleship game and this is my logic to generate the computer ships:

const ComputerBoard = ({ COLUMNS, ROWS }) => {
  const [layout, setLayout] = useState(new Array(ROWS * COLUMNS).fill('empty'));
  const newLayout = [...layout];

  useEffect(() => {
    const checkIfShipFits = (isHorizontal, spaces, i) => {
      let temp = 0;
      const x = i % ROWS;
      const y = Math.floor(i / COLUMNS);

      for (let n = 0; n < spaces; n  = 1) {
        if (isHorizontal) {
          if (x   spaces < COLUMNS && newLayout[i   n] !== 'ship') {
            temp  = 1;
          }
        }
        if (!isHorizontal) {
          if (y   spaces < ROWS && newLayout[i   COLUMNS * n] !== 'ship') {
            temp  = 1;
          }
        }
      }

      return temp === spaces;
    };

    const generateComputerLayout = () => {
      const totalShips = computerShipsAvaibles;
      const boardSize = ROWS * COLUMNS;

      // Iterate over all types of ships
      for (let j = 0; j < totalShips.length; j  = 1) {
        // Iterate over the amount of the specific ship
        for (let k = 0; k < totalShips[j].amount; k  = 1) {
          let i = generateRandomIndex(boardSize);
          const isHorizontal = generateRandomDirection();

          while (!checkIfShipFits(isHorizontal, totalShips[j].spaces, i)) {
            i = generateRandomIndex(boardSize);
          }

          for (let l = 0; l < totalShips[j].spaces; l  = 1) {
            if (isHorizontal) newLayout[i   l] = 'ship';
            if (!isHorizontal) newLayout[i   COLUMNS * l] = 'ship';
          }
        }
      }

      setLayout(newLayout);
    };

    generateComputerLayout();
  }, [COLUMNS, ROWS]);

  Math.floor(Math.random() * (COLUMNS * ROWS));

  return (
    <div>
      <h3>Computer</h3>
      <div className='board'>
        {layout.map((square, index) => (
          <div
            // eslint-disable-next-line react/no-array-index-key
            key={index}
            className={`square ${square} computer`}
          />
        ))}
      </div>
    </div>
  );
};

Currently it's working, but in the developer console throws the warning:

React Hook useEffect has a missing dependency: 'newLayout'. Either include it or remove the dependency array react-hooks/exhaustive-deps

When i add newLayout variable into the dependency array the app crashes due to useEffect multiple re-renders. How can I fix this error? Maybe I'm using the wrong way the useEffect.

CodePudding user response:

You effect is depending on the previous value of the state variable, so instead of having it as a dependency you can do the following:

const ComputerBoard = ({ COLUMNS, ROWS }) => {
  const [layout, setLayout] = useState(new Array(ROWS * COLUMNS).fill('empty'));

  useEffect(() => {
    setLayout((previousLayout) => {
      const newLayout = [...previousLayout];
      ...
      return newLayout;
    }
  }, [COLUMNS, ROWS]);
...

When you want the next state value to depend on the previous one, you should make use of "functional updates", which you can learn more about on the React documentation.

  • Related