'update objects in array with useState (reactJS)

Go to the end of this question for the solution that worked for me!

i'm currently making my own chess game and i want to check if a position already occurred 3 times. I keep the current position in a 2d array. This is what i tried:

Initialize the useState:

const [positionList, setPositionList] = useState([])

structure of the state should look like this

[
   {
      position: //some 2d array,
      counter: 1

   },
   {
      position: //another 2d array,
      counter: 1

   }
]

Compare the positions from the array with the current position (if the position already existed, set counter one up)

let found = false
for (let i = 0; i < positionList.length; i++) {
    if (JSON.stringify(positionList[i].position) === JSON.stringify(position)) {
        positionList[i].counter = positionList[i].counter + 1
        found = true;
        break;
    } 
}

if the position never occurred before, add it to the array

if (found === false) {
    setPositionList([...positionList, {position: position, counter: 1}]);
}

The 2nd and 3rd code block gets executed after every move (after players turn). But for some reason, if "found" is false, it wont add the new position to the array but replace the last object in the array. And furthermore, when i log the changes made to "positionList" with a useEffect, it gets reset after every move to the default value (basically empty array), even though im not changing the state in any other part of my code.

I log the positionList like this:

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

I just cant figure out my error, hopefully someone can help.

Thanks!

EDIT: I decided to try another way of counting the positions. I now only have one array which contains every position that occured in the correct order. Then i just loop through the array and count the amount of times the current position is in the array. My code looks like this:

Note: I think its important to note that you need to create a copy of the array you want to push. Otherwise all entrys in the array will be completly the same and you won't get the expected output (also see here)

function checkForThreefoldRepetition () {
    let counter = 0;
    for (let i = 0; i < positionList.length; i++) {
        if (JSON.stringify(positionList[i]) === JSON.stringify(position)) {
            counter++;
        } 
    }

    if (counter === 2) {
        const updateGameOver = {
            gameOver: true,
            reason: "threefold repetition",
            winner: "draw"
        }
        setGameOver(updateGameOver) 
    }
}

useEffect(() => {
    let positionCopy = [];
    for (let i = 0; i < position.length; i++) {
        positionCopy[i] = position[i].slice();
    }
    setPositionList([...positionList, positionCopy]);

    checkForThreefoldRepetition()
}, [playerTurn])


Solution 1:[1]

I think your problem is you are modifying current state directly like this one which is not a proper way of updating state : positionList[i].counter = positionList[i].counter + 1

So this extra render cause problems,I think.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Yunus TÜRE