I'm creating Sudoku app using React.js.
I've faced an issue of values being written to state sudoku
only after first inner loop of createSudokValues()
(which creates only the first row of sudoku). The outer loop is supposed to generate the rest of the rows (by setSudoku(prevSquares => [ ...prevSquares, ...row]);
).
What am I missing? Thanks!
import React, { useEffect, useState } from 'react';
import '../App.css';
import { SquareType } from '../interfaces/customInterfaces';
import Square from './Square';
function App() {
let row: Array<SquareType> = [];
const [sudoku, setSudoku] = useState<SquareType[]>([]);
const possibleOptionsForDigit = [1, 2, 3, 4, 5, 6, 7, 8, 9];
function generateRandomArrayIndex(unusedDigits: Array<number> ) {
return Math.floor(Math.random() * unusedDigits.length);
}
function unusedDigitInRowAndColumn( sudoku: Array<SquareType>, row: Array<SquareType>, columnIndex: number ) {
let digitsExistingInRow: Array<number> = [];
let digitsExistingInColumn: Array<number> = [];
let unusedDigitsInRow: Array<number> = [];
let unusedDigitsInColumn: Array<number> = [];
let unusedDigits: Array<number>;
let randomDigitFromUnused: number;
digitsExistingInRow = row.map(square => square.digit);
unusedDigitsInRow = possibleOptionsForDigit.filter(digit => !digitsExistingInRow.includes(digit));
digitsExistingInColumn = sudoku.filter(square => square.index === columnIndex).map(square => square.digit);
unusedDigitsInColumn = possibleOptionsForDigit.filter(digit => !digitsExistingInColumn.includes(digit));
unusedDigits = unusedDigitsInRow.filter(digit => unusedDigitsInColumn.includes(digit));
randomDigitFromUnused = unusedDigits[generateRandomArrayIndex(unusedDigits)];
return randomDigitFromUnused;
}
function createSudokValues() {
let generatedUnusedDigit: number = 0;
for ( let y = 1; y <= 9; y ) {
for ( let columnIndex = 1; columnIndex <= 9; columnIndex ) {
while (row.length <= 9) {
console.log('row ', row)
generatedUnusedDigit = unusedDigitInRowAndColumn(sudoku, row, columnIndex);
row.push(
{
digit: generatedUnusedDigit,
index: columnIndex,
shown: true
}
);
break;
}
}
// end of inner loop
// eslint-disable-next-line no-loop-func
setSudoku(prevSquares => [ ...prevSquares, ...row]);
row = [];
// end of outer loop
}
}
useEffect(() => {
console.log('sudoku ', sudoku)
});
useEffect(() => {
createSudokValues();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
return (
<div className="App">
<div className="pageContainer">
<p>
Sudoku
</p>
<div className="sudokuContainer">
{
sudoku.map((square, idx) =>
<Square key={idx} {...square}></Square>
)
}
</div>
</div>
</div>
);
}
export default App;
This is the first row printed to the screen:
Which is equivalent to the first row console.logged:
CodePudding user response:
instead of using a variable inside the function you should initialize the row array every time the first loop runs.
So this function:
function createSudokValues() {
let generatedUnusedDigit: number = 0;
for ( let y = 1; y <= 9; y ) {
for ( let columnIndex = 1; columnIndex <= 9; columnIndex ) {
while (row.length <= 9) {
console.log('row ', row)
generatedUnusedDigit = unusedDigitInRowAndColumn(sudoku, row, columnIndex);
row.push(
{
digit: generatedUnusedDigit,
index: columnIndex,
shown: true
}
);
break;
}
}
// end of inner loop
// eslint-disable-next-line no-loop-func
setSudoku(prevSquares => [ ...prevSquares, ...row]);
row = [];
// end of outer loop
}
}
should be:
function createSudokValues() {
let generatedUnusedDigit = 0;
let row: Array<SquareType> = [];
let tempSudoku: Array<SquareType> = [];
for (let y = 1; y <= 9; y ) {
for (let columnIndex = 1; columnIndex <= 9; columnIndex ) {
while (row.length <= 9) {
console.log("row ", row);
generatedUnusedDigit = unusedDigitInRowAndColumn(
sudoku,
row,
columnIndex
);
row.push({
digit: generatedUnusedDigit,
index: columnIndex,
shown: true
});
break;
}
tempSudoku.push(...row);
row = [];
}
}
setSudoku(tempSudoku);
}
It makes little sense to setState inside the loop because it would be bad for performance reasons, since state variable would trigger useless renders.
And also you should remove the let row: Array<SquareType> = [];
initialization at the beginning of the App component.