Reactjs - generating array dynamically breaks unique code results

Viewed 76

I'm trying to make a clone of Wordle but more dynamic.

I have a "normal" working version but it is limited by characters of a word and number of attempts. This restriction is what I am trying to remove.

In my main class I set an array[] board here stores both the number of attempts allowed and allocated length based on correctWord character length.

[[attempt1],[attempt2]...etc] represents board child arrays structure.

Its child arrays of characters by attempt1[char1,char2,char3,char4,char5 ...etc]

The original working implementation (which is static):

 const [board, setBoard] = useState([["","","","",""],["","","","",""]]);

Instead I am trying to do the following (to make it dynamic):

    const correctWord = "RIGHT";
    const [lives, setLives] = useState(2);
     const [board, setBoard] = useState(Array(lives).fill(Array(correctWord.length).fill("")));

When I do this and play (entering a character) It will modify the correct character, but for every array, rather than the only one in current selection

Here are images which show both implementation results clicking characters:

Static impl: enter image description here

Dynamic impl: enter image description here

Finally the logic for selecting a character is the following:

  const selectKey = (event) => {
    if (current.letterPos >= correctWord.length) return; // dont add more than correct word
    const newBoard = [...board];
    newBoard[current.attempt][current.letterPos] = key;
    setBoard(newBoard);
    setCurrent({ attempt: current.attempt, letterPos: current.letterPos + 1 }); // increment
  };
    }

I was told that this is incorrect because it is doing state mutation, but if this is true is why does it work before making it dynamic?

The folowing is a codesandbox where this issue can be visualised, it has been stripped to its bare essentials and inputs 1 character: https://codesandbox.io/s/shy-rain-8w7e3w?file=/src/App.js:505-904

The line in App.js 21 is where the working non-dynamic assignment happens and works.

When you comment out line 9+10 to make the array empty and uncomment line 14 to set the array this is where the issue can be seen.

Also with F12 console debug it will show the state of elements the board which also shows this issue.

Any suggestions are welcome

1 Answers

Turns out it was 2 issues.

The first being the assignment of board (which I thought it was) and secondly the selectLetter functionality.

I was provided this answer and will not take credit for it. The following included explanation:

Array(lives).fill(Array(correctWord.length).fill(""))

When you execute that, these things happen:

  1. Array(correctWord.length).fill("") creates an array and fills it with empty strings
  2. Array(lives).fill(…) creates a second array and fills it with the first array lives times

So all the elements of that second (outer) array are the exact same array. Changing that array at any index will affect all indices because they're all the same array However, if you fix the problem that you're mutating state, this wouldn't matter anymore because the issue only matters with mutation.

But FYI here's how you could create separate arrays.

Array.from({ length: lives }, () =>
  Array.from({ length: correctWord.length }, () => "")
)

Then: line 2 [newBoard[current.attempt][current.letterPos] = key;] is basically the issue. Here's one approach to fix that issue.

const newAttempt = [...board[current.attempt]];
newAttempt[current.letterPos] = key;
const newBoard = [...board];
newBoard[current.attempt] = newAttempt;
setBoard(newBoard);
Related