0

I'm kind of following this YouTube tutorial to make my own Tetris game in react. I'm doing this to train my react skills. I'm trying to come up with my own solutions and sometimes check what Thomas did in the tutorial.

I'm having a problem now when I try to populate the map grid with is a 2d array to change the color of one single cell, it change the whole row of cells.

const STAGE_WIDTH = 12;
const STAGE_HEIGHT = 20;

const stageCells = Array.from(Array(STAGE_HEIGHT), () =>
  new Array(STAGE_WIDTH).fill([0, 1])
)

stageCells[0][0].color = "blue";
console.log(stageCells);

return (
  <StageGrid>
    {stageCells.map(cells => cells.map((cell, key) =>
      <Cell key={key} color={cell.color} />
    ))}
  </StageGrid>
)

The console.log shows that its changing all the items in the 0 position, I only what to change one.

Console output:

consolelog

Mr. Polywhirl
  • 42,981
  • 12
  • 84
  • 132
Fafnir
  • 3
  • 1
  • You're filling the entire array with the same object, `[0, 1]`, so mutating that object mutates all entries in the array. You should fill the array with unique objects that look like `[0, 1]`. This doesn't have anything to do with react. – CollinD Feb 27 '23 at 17:17
  • `const stageCells = Array.from(Array(STAGE_HEIGHT), () => new Array(STAGE_WIDTH).fill().map(() => [0, 1]) )` – Konrad Feb 27 '23 at 17:32

1 Answers1

0

Try to establish your initial matrix correctly:

const initialStageCells = Array.from({ length: STAGE_HEIGHT }, () =>
  Array.from({ length: STAGE_WIDTH }, () => [0, 1]));

Example

const { useEffect, useState } = React;

const STAGE_WIDTH = 7, STAGE_HEIGHT = 12;

const StageGrid = ({ children }) => (
  <div className="StageGrid">{children}</div>
);

const Cell = ({ color }) => (
  <div className="Cell" style={{ background: color }}></div>
);

const TetrisApp = () => {
  const [stageCells, setStageCells] = useState([]);
  
  useEffect(() => {
    const initialStageCells = Array.from({ length: STAGE_HEIGHT }, () =>
      Array.from({ length: STAGE_WIDTH }, () => [0, 1]));
    setStageCells(initialStageCells);
    
    // After 1 second, set position of first Tetromino
    setTimeout(() => {
      setStageCells((currStageCells) => {
        const copy = currStageCells.map(rows => [ ...rows ]);

        currStageCells[0][0].color = 'cyan';
        currStageCells[1][0].color = 'cyan';
        currStageCells[2][0].color = 'cyan';
        currStageCells[3][0].color = 'cyan';

        return copy;
      });
    }, 1000);
    
    // After 2 seconds, move first Tetromino down by 1
    setTimeout(() => {
      setStageCells((currStageCells) => {
        const copy = currStageCells.map(rows => [ ...rows ]);

        currStageCells[0][0].color = undefined;
        currStageCells[1][0].color = 'cyan';
        currStageCells[2][0].color = 'cyan';
        currStageCells[3][0].color = 'cyan';
        currStageCells[4][0].color = 'cyan';

        return copy;
      });
    }, 2000);
  }, []);
  
  return (
    <StageGrid>
      {stageCells.map((row, rowIndex) => (
        <div key={`row-${rowIndex}`}>
          {row.map((cell, key) =>
            <Cell key={key} color={cell.color} />
          )}
        </div>
      ))}
    </StageGrid>
  )
};

ReactDOM
  .createRoot(document.getElementById('root'))
  .render(<TetrisApp />);
*, *::before, *::after { box-sizing: border-box; }

html, body, #root {
  width: 100%;
  height: 100%;
  margin: 0;
  padding: 0;
}

#root {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  background: #222;
}

.StageGrid {
  display: flex;
  flex-direction: column;
  background: #000;
  grid-row-gap: 1px;
}

.StageGrid div {
  display: flex;
  grid-column-gap: 1px;
}

.Cell {
  width: 12px;
  height: 12px;
  display: inline-flex;
}
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.2.0/umd/react.development.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.2.0/umd/react-dom.development.js"></script>
Mr. Polywhirl
  • 42,981
  • 12
  • 84
  • 132