Xelphin
Xelphin

Reputation: 349

useEffect() Hook doesn't update when State Change

I have the following code:

import React, { useState, useEffect} from 'react';
import Board from './Board';
import Player from '../functions/Player'

function Game() {

  const [computer, setComputer] = useState(Player(1,false));
  const [computerBoard, setComputerBoard] = useState(new Array(100).fill(-1));

  const sendAttack = (location) => {
    // Activates everytime I click Board component
    let compCopy = computer;
    compCopy.receiveAttack(location);
    setComputer(compCopy);
    setComputerBoard(compCopy.getHitBoard());
    console.log(compCopy.getHitBoard()); // always prints, each time I do sendAttack()
    // and the hitBoard does change, shows a different hitBoard everytime I click!
  }

  useEffect(() => {
    console.log('something has changed') // print (twice for some reason) only on render, why?
    
  })

  useEffect(() => {
    console.log('computer hit has changed') // prints only on render and on the first setAttack()
  },[computer]);

  useEffect(() => {
    console.log('computer board has changed') // prints on render
    // and once it works but only on first sendAttack()
  },[computerBoard])

  return (
    <div className="game">
      <Board hitBoard ={computer.getHitBoard()} sendAttack={sendAttack} />
    </div>
  );
}

export default Game;

The problem is that I want that the Board components hitBoard prop will keep getting updated whenever sendAttack() has finished updating. But it never does update it except on render (and actually just one time for the first sendAttack()). I don't understand why when I setComputer it doesn't initiate a useEffect()

Is there an obvious mistake in my code? Thanks for the tips.

Upvotes: 0

Views: 1176

Answers (2)

James Clover
James Clover

Reputation: 74

The answer given by @Atmas above, is correct. I'd tweak it a bit by having receiveAttack() return a new object that is a copy of the one passed in with the additional changes. That's a more robust solution to data management in general - don't modify objects, make new ones and return them. Also, that idea of comparing object IDs rather than the content of the object is something you're going to hit all the time in React and various React libraries.

Here's a useful guide that talks about exactly what you're trying to do - use an object as a React state variable:

https://blog.logrocket.com/a-guide-to-usestate-in-react-ecb9952e406c/#usinganobjectasastatevariablewithusestatehook

Also, you don't need a useEffect() to debug this stuff. Just a regular ol' console log placed right above your return statement will print when the component re-renders.

Upvotes: 2

Atmas
Atmas

Reputation: 2393

I don't think your compCopy is actually a new object. If the object isn't different then setComputer likely won't be different therefore the effect won't trigger. Try something like let compCopy = makeANewCopyOf(computer) such that compCopy is a brand new object that you have cloned yourself.

Upvotes: 2

Related Questions