Home > Blockchain >  React function is called twice instead of once
React function is called twice instead of once

Time:11-26

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

CodePudding user response:

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]);

CodePudding user response:

@mcanzerini right, in useEffect you can calling createSudokValues and in jsx use row without calling second time createSudokValues, just do row.map() instead of createSudokValues().map(). But you must use hook useState. Example:

const [row, setRow] = 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));
                    setRow(prev => {
                       return {
                           ...prev,
                           {
                              id: idIncremented,
                              digit: generatedUnusedDigit,
                              index: columnIndex,
                              shown: true
                            }
                       }
                    })

                    idIncremented  ;
                    break;
                }
            }
            previuosRows = [ ...sudoku];
            sudoku = [ ...previuosRows, ...row ];
            row = [];
        }
        return sudoku;
    }
useEffect(() => {
    createSudokValues();
    // eslint-disable-next-line react-hooks/exhaustive-deps
}, []);

JSX:

{
    row.map((square, idx) =>
        <Square key={idx}></Square>
    )
}
  • Related