rumon
rumon

Reputation: 606

React function is called twice instead of once

I'm creating Sudoku game app using React. Currently I'm creating the functions which generates sudoku squares (creates random numbers for them that don't exist in their row and column).

The problem is that calling that function from useEffect() and consoling out sudoku squares generated, it generates 2 arrays of squares instead of one. If I run these functions in plain JS instead of react, everything works as expected and 81 square is generated.

What am I missing? Thanks!

import React, { useEffect } from 'react';
import '../App.css';
import Square from './Square';

function App() {

    type SquareType = {
        id: number;
        digit: number;
        index: number;
        shown: boolean;
    }

    let row: Array<SquareType> = [];
    let previuosRows: Array<SquareType> = [];
    let sudoku: Array<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 idIncremented: number = 0;
        let generatedUnusedDigit: number = 0;
        for ( let y = 1; y <= 9; y++ ) {
            for ( let columnIndex = 1; columnIndex <= 9; columnIndex++ ) {
                while (row.length <= 9) {
                    generatedUnusedDigit = unusedDigitInRowAndColumn(sudoku, row, columnIndex);
                    //console.log("unusedDigitInRowAndColumn ", unusedDigitInRowAndColumn(sudoku, row, columnIndex));
                    row.push(
                        {
                            id: idIncremented,
                            digit: generatedUnusedDigit,
                            index: columnIndex,
                            shown: true
                        }
                    );
                    idIncremented++;
                    break;
                }
            }
            previuosRows = [ ...sudoku];
            sudoku = [ ...previuosRows, ...row ];
            row = [];
        }
        return sudoku;
    }

    useEffect(() => {
        console.log(createSudokValues())
        // eslint-disable-next-line react-hooks/exhaustive-deps
      }, []);
    return (
        <div className="App">
            <div className="pageContainer">
                <p>
                    Sudoku
                </p>
                <div className="sudokuContainer">
                    {
                        createSudokValues().map((square, idx) =>
                        <Square key={idx}></Square>
                        )
                    }
                </div>
            </div>
        </div>
    );
}

export default App;

These are square objects printed out to the console (arrays with items [0 - 99] and [100 - 161] are generated although only [0 - 99] should be generated):

enter image description here

Upvotes: 0

Views: 2048

Answers (2)

Vitaliy Rayets
Vitaliy Rayets

Reputation: 2394

@mcanzerini right, in useEffect you can calling createSudokValues and in jsx render sudoku without calling second time createSudokValues. You must use hook useState for sudoku to rerender the state in JSX while updating it. Then just do sudoku.map() instead of createSudokValues().map(). Example:

const [sudoku, setSudoku] = useState([]);


function createSudokValues() {
        let idIncremented: number = 0;
        let generatedUnusedDigit: number = 0;
        for ( let y = 1; y <= 9; y++ ) {
            for ( let columnIndex = 1; columnIndex <= 9; columnIndex++ ) {
                while (row.length <= 9) {
                    generatedUnusedDigit = unusedDigitInRowAndColumn(sudoku, row, columnIndex);
                    //console.log("unusedDigitInRowAndColumn ", unusedDigitInRowAndColumn(sudoku, row, columnIndex));
                    row.push(
                    {
                        id: idIncremented,
                        digit: generatedUnusedDigit,
                        index: columnIndex,
                        shown: true
                    }
                );

                    idIncremented++;
                    break;
                }
            }
            setSudoku(prev => { 
               return [ ...prev, ...row]
            });
            row = [];
        }
}

useEffect(() => {
    createSudokValues();
}, []);

JSX:

{
    sudoku.map((square, idx) =>
        <Square key={idx}></Square>
    )
}

Upvotes: 1

Yash Sonalia
Yash Sonalia

Reputation: 398

One obvious explanation is that Chrome console breaks an array of length > 100 into multiple parts,i.e., if the array is of length 210, console will log something like this:

> [0-99] 
> [100-199]
> [200-209]
length: 210

So if the array is of length 162, it will display them as the way you're getting.

or

If you're expecting 100 values,

This may be happening because of the empty dependency list in the useEffect hook. Try passing a constant value to it.

For eg:

useEffect(callback, [null]);
useEffect(callback, [1]);

Upvotes: 1

Related Questions