简体   繁体   中英

useEffect not re-run after updating state of 2D Array matrix

I have a useState with an initial value of an array, and I have an algorithm which changes the array.

I console.log 'ed the matrix and it changed, but the useEffect connected to it, which has the dependency of the matrix from useState , does not update my components like I need it to.

It only changes and re-renders when I start typing in VS Code.

I tried to switch the setMatrix from useState around but it did not work.

import React, {MouseEvent, useEffect, useState} from 'react'

const TwoDArray = () => {
 let twodarray:number[][] =  [
    [1,1,1,1],
    [1,0,1,1],
    [1,1,0,1],
    [0,0,0,1]
    ] 
  const [matrix, setMatrix] = useState(twodarray)
  
  const setMatrixZeros = (matrix: number[][], ) => { 
    let col0: number = 1, rows: number = matrix.length, cols = matrix[0].length
    
    for (let i: number = 0; i < rows; i++) {
      if (matrix[i][0] == 0) col0 = 0
      for (let j: number = 1; j < cols; j++) 
        if(matrix[i][j]==0) matrix[i][0] = matrix[0][j] = 0
    }
    
    for (let i: number = rows - 1; i >= 0; i--) { 
      for (let j: number = cols - 1; j >= 1; j--) {
        if (matrix[i][0] == 0 || matrix[0][j] == 0) matrix[i][j] = 0
      }
      if(col0==0) matrix[i][0] = 0 
    }
  }

  const handleClick = (e: MouseEvent<HTMLButtonElement>) => {
    setMatrixZeros(matrix)
      
  }  
   
  useEffect(() => {
  }, [matrix]); // Only re-run the effect if matrix changes
  
console.log(matrix)
  return (
    <div className="twodarray">
      <div className="matrix">
        {    matrix.map((item, index) => { 
          return <div key={index} className='box-container'>
            {item.map((i, idx) =>
              <input type='text' key={idx} className='box' value={ i} readOnly/>)}</div>
    })}
      </div>
      <button onClick={handleClick }>Set Matrix Zeroes</button>
    </div>
  ) 
}

export default TwoDArray

React does not track deep state mutation (unlike Vue 3 for example).

You have to explicitly change the state using the setMatrix set state action + make sure the new state is a different reference, eg with a shallow clone (typically using a spread operator):

const handleClick = (e: MouseEvent<HTMLButtonElement>) => {
  setMatrixZeros(matrix)
  // Create a new reference
  const newMatrix = [...matrix]
  // Make sure to use the setter
  setMatrix(newMatrix)
}

Without the new reference, the dependency array would not see the state change, and the useEffect would not re-run.

The big problem, as I understand it, is that the code is modifying the state directly, ex: matrix[i][0] = matrix[0][j] , instead of using setMatrix(newMatrix) .

If you call setMatrix(Matrix) , with the already modified matrix (as I'm guessing you did before switching to useEffect ) it won't notice a change and will not re-render the page.

Below is a working, modified version of your code, that just switches places of the ones and zeroes. I didn't really get what your setMatrixZeroes() was suposed to do, so I changed the name to changeMatrix() , as to not confuse it with a state-setting function, and you can just modify it to fit the functionality you're after.

const TwoDArray = () => {
  let initMatrix:number[][] = [
    [1,1,1,1],
    [1,0,1,1],
    [1,1,0,1],
    [0,0,0,1]
  ]
  const [matrix, setMatrix] = useState(initMatrix)

  const changeMatrix = () => {
    const newMatrix:number[][] = []

    for(let row of matrix){
      let newRow:number[] = []
      for(let number of row){
        number === 0
          ? newRow.push(1)
          : newRow.push(0)
      }
      newMatrix.push(newRow)
    }
    setMatrix(newMatrix)
  }

  const handleClick = (e: MouseEvent<HTMLButtonElement>) => {
    changeMatrix()
  }

  return(
   // ... tsx-code ... //
  )
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM