Home > Net >  Updating specific values of matrix useState hook state variable
Updating specific values of matrix useState hook state variable

Time:10-05

I am trying to update a matrix of 20x20 values, each slot is binded to a div, once a div is clicked, its background color should change.

What is the best way of doing this, without having to set the whole matrix?

Also, why do some set methods don't work, since most of these are used in examples through the internet?

import { useState } from "react";

function App() {
  const n = 20;

  // define [20][20] array with default value on each slot as { color: "red" }
  const [matrix, setMatrix] = useState(Array(n).fill(Array(n).fill({ color: "red" })))

  let updateCell = (i, j) => {
    console.log(i, j); // Works perfectly, returns i and j of the matrix slot binded to the clicked cell.

    // const mat = matrix; mat[i][j].color = "blue";                  // Doesnt work
    // setMatrix((mat) => { mat[i][j].color = "blue"; return mat; }); // Doesnt work
    // matrix[i][j].color = "blue"; setMatrix(matrix);                // Doesnt work
    // matrix[i][j].color = "blue"; setMatrix([...matrix]);           // Changes ALL squares instead of the one clicked.
    // const mat = Object.assign({}, matrix); setMatrix(mat);         // TypeError: matrix.map is not a function
  }

  return (

    <div className="container">

      {/* Renders all squares fine! */}
      {matrix.map((i, ipos) => {
        console.log(matrix.length);

        return (
          <div key={ipos} className="d-flex flex-row wrap">

            {i.map((j, jpos) => {
              console.log(i.length);
              return (

                <div
                  style={{ backgroundColor: matrix[ipos][jpos].color, minWidth: "5%", minHeight: "50px", textAlign: "center", color: "white" }}
                  key={ipos   ", "   jpos}
                  onClick={() => { updateCell(ipos, jpos) }}
                >
                  {ipos   ", "   jpos}
                </div>

              )
            })}

          </div>
        )

      })}

    </div>
  )
}

export default App;

Best regards, and thank you.

CodePudding user response:

Answer made with the helps these comments (Chris G and RCC):

You're changing state in place; React won't update the component unless it's a completely new matrix array. The fourth variant should have no effect either. The fifth changes matrix from an array to an object, those don't have a map function since they aren't arrays.

You need to update useing the setMatrix function, and need to pass a new Array to that function (when using hooks, always need to be a new reference/object)

Yeah, the problem is how you're filling the array. { color: "red" } creates a single object, and all array elements contain/point to that single object. Changing the color property will do that for all elements.

  useEffect(() => {
    const n = 20;

    for (let i = 0; i < n; i  ) {
      matrix[i] = [];
      for (let j = 0; j < n; j  ) {
        matrix[i][j] = { color: "red", coords: [i, j] }
      }
    }

    setMatrix([...matrix]);

  }, [])

  let updateCell = (i, j) => {
    console.log(i, j);
    matrix[i][j].color = "blue";
    setMatrix([...matrix]);
  }

CodePudding user response:

You need to update useing the setMatrix function, and need to pass a new Array to that function (when using hooks, always need to be a new reference/object):

const updateCell = (i, j) => {
  let newMatrix = matrix.slice(); // just to create a copy of the matrix
  newMatrix[i][j].color = "blue";
  setMatrix( newMatrix ); // this call will trigger a new draw
}

EDIT:
Also, Array(n).fill will put the same reference on every place of the matrix, so changing one value will update all of then (because all are the same value / reference).

Change the line

useState(Array(n).fill(Array(n).fill({ color: "red" })))

to:

useState( Array.from({ length: 20 }, () => Array.from({length: 20}, () => ({ color: 'red' })) ) )
  • Related