0

I am working on developing a game and need to increment a state variable when a player clicks on the game board with a valid move. The functionality for validating the move and making the move is all in place; however, I am currently unable to increment the state variable by using the corresponding hook.

I have been using the same method of incrementing the variable as defined within the React docs on state hooks: https://reactjs.org/docs/hooks-state.html


I initialise the state variable here:

    const [placedShips, setPlacedShips] = useState(0);

Next, a click event listener is applied to each cell within the gameboard:

const setGameboardUp = () => {
        const gameboardArray = Array.from(document.querySelectorAll(".cell"));
        gameboardArray.forEach((cell) => {
            cell.addEventListener("click", (e) => {
                e.stopImmediatePropagation();
                let direction = currentShip().direction;
                let start = parseInt(cell.id);
                let end = start + currentShip().length - 1;
                if (playerGameboard.checkValidCoordinates(direction, start, end)) {
                    playerGameboard.placeShip(placedShips, direction, start, end);
                    console.log(placedShips + 1)
                    setPlacedShips(placedShips + 1);
                    console.log(placedShips)
                }
            });

Unfortunately, the "placedShips" state variable remains 0 and so my game is not functioning correctly. I have tried utilising the following function and it still remains 0:

setPlacedShips(1);

I'm a bit lost as to why this is happening and have not had any luck attempting several other fixes. For reference, below is the full component I am utilising:

let playerGameboard = gameboardFactory();

const GameboardSetup = () => {
    const [humanSetupGrid, setHumanSetupGrid] = useState([]);
    const [ships,_setShips] = useState([
        {
            name: 'carrier',
            length: 5,
            direction: 'horizontal'
        },
        {
            name: 'battleship',
            length: 4,
            direction: 'horizontal'
        },
        {
            name: 'cruiser',
            length: 3,
            direction: 'horizontal'
        },
        {
            name: 'submarine',
            length: 3,
            direction: 'horizontal'
        },
        {
            name: 'destroyer',
            length: 2,
            direction: 'horizontal'
        }]);
    const [placedShips, setPlacedShips] = useState(0);

    const createGrid = () => {
        const cells = [];
        for (let i = 0; i < 100; i++) {
            cells.push(0);
        };
    };
    
    const createUiGrid = () => {
        const cells = [];
        for (let i = 0; i < 100; i++) {
                cells.push(i);
        }
        let counter = -1;
        const result = cells.map((cell) => {
            counter++;
            return <div className='cell' id={counter} />;
        });
        setHumanSetupGrid(result);
    };

    const setUpPlayerGrid = () => {
        // createGrid('grid');
        createUiGrid();
    }

    const currentShip = () => {
        return ships[placedShips];
    };

    const setGameboardUp = () => {
        const gameboardArray = Array.from(document.querySelectorAll(".cell"));
        gameboardArray.forEach((cell) => {
            cell.addEventListener("click", (e) => {
                e.stopImmediatePropagation();
                let direction = currentShip().direction;
                let start = parseInt(cell.id);
                let end = start + currentShip().length - 1;
                if (playerGameboard.checkValidCoordinates(direction, start, end)) {
                    playerGameboard.placeShip(placedShips, direction, start, end);
                    console.log(placedShips + 1)
                    setPlacedShips(placedShips + 1);
                    console.log(placedShips)
                }
            });
            cell.addEventListener("mouseover", (e) => {
                e.stopImmediatePropagation();
                let direction = currentShip().direction;
                let start = parseInt(cell.id);
                let end = start + currentShip().length - 1;
                if (currentShip().direction === 'horizontal') {
                    const newShip = [];
                    if (playerGameboard.checkValidCoordinates(direction, start, end)) {
                        for (let i = start; i <= end; i++) {
                            newShip.push(i);
                        };
                        newShip.forEach((cell) => {
                            gameboardArray[cell].classList.add('test');
                        })
                    }
                } else {
                    const newShip = [];
                    if (playerGameboard.checkValidCoordinates(direction, start, end)) {
                        for (let i = start; i <= end; i += 10) {
                            newShip.push(i);  
                        };
                        newShip.forEach((cell) => {
                            gameboardArray[cell].classList.add('test');
                        })
                    }
                }
            })
            cell.addEventListener("mouseleave", (e) => {
                e.stopImmediatePropagation();
                let direction = currentShip().direction;
                let start = parseInt(cell.id);
                let end = start + currentShip().length - 1;
                if (currentShip().direction === 'horizontal') {
                    const newShip = [];
                    if (playerGameboard.checkValidCoordinates(direction, start, end)) {
                        for (let i = start; i <= end; i++) {
                            newShip.push(i);
                        };
                        newShip.forEach((cell) => {
                            gameboardArray[cell].classList.remove('test');
                        })
                    }
                } else {
                    const newShip = [];
                    if (playerGameboard.checkValidCoordinates(direction, start, end)) {
                        for (let i = start; i <= end; i += 10) {
                            newShip.push(i);  
                        };
                        newShip.forEach((cell) => {
                            gameboardArray[cell].classList.remove('test');
                        })
                    }
                }
            })
        });
    };

    useEffect(() => {
        setUpPlayerGrid();
        // setUpComputerGrid();
    }, []);

    useEffect(() => {
        console.log(humanSetupGrid)
    }, [humanSetupGrid]);

    // Re-render the component to enable event listeners to be added to generated grid
    useLayoutEffect(() => {
        setGameboardUp();
    });

    return (
        <div className="setup-container">
            <div className="setup-information">
                <p className="setup-information__p">Add your ships!</p>
                <button className="setup-information__btn">Rotate</button>
            </div>
            <div className="setup-grid">
                <Table grid={humanSetupGrid} />
            </div>
        </div>
    );
}

export default GameboardSetup;

If anybody has any ideas of what could be going wrong here, I would be super appreciative of any help!

Thank you.

Tjm92
  • 167
  • 2
  • 19

1 Answers1

3

useState does not immediately reflect the changes that you make to it. See: useState set method not reflecting change immediately

If you expect something to happen as a result of placedShips changing values, you should use useEffect with placedShips as one of the dependencies:

useEffect(() => {
  // do something here once placedShips has changed
}, [placedShips])

Note that if you log placedShips inside that useEffect, it will show the updated value.

jnpdx
  • 45,847
  • 6
  • 64
  • 94
  • Perfect explanation. See this question too: https://stackoverflow.com/questions/55983047/strange-behavior-of-react-hooks-delayed-data-update – Hernã Saldanha Mar 04 '21 at 23:13
  • Thank you - I also have an issue with the event listeners holding the old state, only enabling it to increment to 1. Believe I need to remove them all and then reapply using the method you have mentioned above. – Tjm92 Mar 04 '21 at 23:17
  • You can try using `setPlacedShips(oldValue => oldValue + 1)` which will give you access to the previous value when you set then new one. – jnpdx Mar 04 '21 at 23:58