React useState Help - Update item in Array of Objects

Hello,

I’m creating a game where you have to roll ten dice and get their numbers to match. You can reroll the dice and you can click on a die to freeze it (keep it’s number/don’t reroll)

I’m storing dice data in a useState that holds an array of objects

When I try to update the useState object by directly reversing the die’s isFrozen boolean, it doesn’t work. Instead, it works when I make a copy of the die object, update it, and return it

I don’t understand why I can’t directly update the die object in the array. Why do I have to create a copy of the die object?

useState

    const [dice, setDice] = useState(() => {
        const diceArray = [];
        for (let i = 1; i <= 10; i++) {
            diceArray.push({ id: nanoid(), num: rollDice(), isFrozen: false });
        }
        return diceArray;
    });

freezeDie code - Doesn’t work

    const freezeDie = (id) => {
        setDice((oldDice) =>
            oldDice.map((die) => {
                if (die.id === id) {
                    console.log("die", die);
                    console.log("");
                    die.isFrozen = !die.isFrozen; // THIS LINE
                }
                return die;
            })
        );
    };

Result

  • the first click reverses the isFrozen boolean (it freezes the die - intended)
  • future clicks reverses the isFrozen boolean twice (basically nothing changes - not intended)

freezeDie code - Works

    const freezeDie = (id) => {
        setDice((oldDice) =>
            oldDice.map((die) => {
                if (die.id === id) {
                    console.log("die", die);
                    console.log("");
                    return { ...die, isFrozen: !die.isFrozen };  // THIS LINE
                }
                return die;
            })
        );
    };

Result

  • the first click reverses the isFrozen boolean (it freezes the die - intended)
  • future clicks also reverse the isFrozen boolean but call the function twice (not sure why it’s being called twice)

React docs: Updating objects inside arrays

I read through the React doc, and I understand why we want to create a copy of the object in the useState array

But I’m still confused about why React is re-rendering twice when I click on a Die component