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
CodePudding user response:
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, e.g. 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.
CodePudding user response:
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 ... //
)
}